Bond yield and Z-spread analytics¶
Go and Python expose the existing Rust BondFunctions and CashFlows routes.
Python methods live on the shared Bond base and are inherited by fixed, zero
and floating-rate bonds. There is no separate BondFunctions namespace class.
The engine-based clean_price() and dirty_price() methods are unchanged.
Python example¶
An explicit settlement is an actual date, not an evaluation date to advance. Fixed/zero analytical queries below need neither an engine nor an evaluation date.
from itofin import Settings
from itofin.instruments import ZeroCouponBond
from itofin.termstructures import FlatForward
from itofin.time import Calendar, Compounding, Date, DayCounter, Frequency
settings = Settings()
settlement = Date(15, 1, 2026)
curve = FlatForward(Date(1, 1, 2026), 0.035, DayCounter.actual365_fixed())
bond = ZeroCouponBond(
2, Calendar.null_calendar(), 1000.0, Date(1, 1, 2028), settings,
)
price = bond.clean_price_at_z_spread(
curve, 0.007, Compounding.Compounded, Frequency.Semiannual,
settlement=settlement,
)
spread = bond.z_spread(
price, curve, Compounding.Compounded, Frequency.Semiannual,
settlement=settlement,
)
print(f"Clean price per 100: {price:.12f}")
print(f"Recovered Z-spread: {spread:.12f}")
Output:
The equivalent checked Go example is
sdk/go/examples/bond-analytics/main.go.
Go methods return (float64, error); all related handles must belong to one
Session. Python returns float or raises the existing typed binding errors.
Bond routes¶
Python on Bond |
Go on *Bond |
Inputs beyond the bond |
|---|---|---|
clean_price_from_curve |
CleanPriceFromCurve |
Discount curve |
dirty_price_from_curve |
DirtyPriceFromCurve |
Discount curve |
clean_price_from_yield |
CleanPriceFromYield |
Yield, day counter, compounding, frequency |
dirty_price_from_yield |
DirtyPriceFromYield |
Yield, day counter, compounding, frequency |
yield_rate |
YieldRate |
Quote and yield conventions |
clean_price_at_z_spread |
CleanPriceAtZSpread |
Curve, spread, compounding, frequency |
dirty_price_at_z_spread |
DirtyPriceAtZSpread |
Curve, spread, compounding, frequency |
z_spread |
ZSpread |
Quote, curve, compounding, frequency |
- The price outputs/quote targets in the table above are per 100 of current settlement notional.
Clean quotes exclude accrued interest; dirty quotes include it. Inversions
accept
price_type=BondPriceType.Cleanby default, or explicitDirty. - Python
settlement=Noneresolves through the existing checked settlement method, including lag and issue-date floor. A supplied settlement is passed unchanged, with no extra advance or issue-date adjustment. - Yield pricing uses the core's stepwise cash-flow discount times. It does not replace the leg with one terminal discount factor.
- Spread routes use the curve's own day counter. They do not accept a separate day counter or change the original curve.
- Nontradable/matured analytical bonds return an error. This is different from the existing expired engine quote, which can return zero.
Python optional settlement and solver controls are keyword-only. Go supplies
BondYieldConfig, BondYieldSolveConfig, BondZSpreadConfig or
BondZSpreadSolveConfig, with SettlementDate and convention fields.
Leg routes¶
Python functions live in itofin.cashflows; Go methods live on *Leg:
| Python function | Go method | Meaning |
|---|---|---|
npv_at_z_spread |
NPVAtZSpread |
Spreaded NPV in currency units |
z_spread |
ZSpread |
Spread repricing a currency NPV target |
The Python argument order is:
npv_at_z_spread(leg, curve, spread, compounding, frequency, settings)
z_spread(leg, target_npv, curve, compounding, frequency, settings)
Both functions accept keyword-only include_settlement_date_flows,
settlement_date and npv_date. Inclusion is tri-state: None preserves Settings
policy, while explicit false/true requests exclusion/inclusion. The today's-flow
Settings override remains effective. Omitted settlement uses the supplied
Settings evaluation date; omitted NPV date uses settlement. Explicit dates are
not business-day adjusted. Valid empty-leg NPV remains zero without requiring an
evaluation date; spread inversion follows the unchanged core solver behavior.
Conventions and solvers¶
itofin.time.Compounding and Go Compounding share these explicit integers:
| Convention | Value | Behavior |
|---|---|---|
Simple |
0 | Simple accumulation |
Compounded |
1 | Periodic compounding |
Continuous |
2 | Continuous compounding |
SimpleThenCompounded |
3 | Simple at/below 1/f, periodic above |
CompoundedThenSimple |
4 | Periodic at/below 1/f, simple above |
Existing Frequency facade ordinals stay Annual=0, Semiannual=1, Quarterly=2, Monthly=3; these map to native frequencies 1, 2, 4 and 12. Conventions are supplied by the caller, never inferred from coupon frequency. Admissible negative rates work; nonpositive simple factors/periodic bases and nonfinite inputs error.
Solver defaults are accuracy 1e-10, maximum evaluations 100, yield guess
0.05 and spread guess 0.0. Go nil solver fields select those defaults. Explicit
zero accuracy/count rejects. An explicit zero guess remains zero: the unchanged
yield solver brackets with guess/10, so a nonzero root with zero guess can fail
typed rather than silently selecting the default. A genuine zero-yield target
can return zero. Solver failures do not poison later queries on the same bond.
Live floating dependencies and cache purity¶
Analytical queries are independent of the engine's lazy state. Frozen and already-calculated bonds use current coupon market inputs for these queries, without changing cached policy flags, frozen state, calculated state or engine NPV. External quote notifications can themselves invalidate calculated state; queries preserve the state present immediately before the analytical call.
Guards inspect only surviving, non-ex-coupon flows. Clean outputs and Clean inversion also inspect the actual accrued coupon group, including negative ex-coupon accrual. Dirty routes do not require unrelated accrued-coupon data. Retained Leg views keep their original coupon positions and see later pricer replacement. Redemption views do not acquire a floating-coupon guard.
Actual floating fixings still need their index Settings evaluation date and required history/pricer/volatility. All-zero gearing has fixed cashflows and avoids those irrelevant requirements. The Rust at-par/indexed policy is read live; native QuantLib pricers sample policy, so policy oracles use separately constructed native cases rather than a global-toggle assumption.
Verification and scope¶
Tracked QuantLib 1.43 fixtures cover all routes, both quote types, five compounding modes, four frequencies, hybrid thresholds, negative rates, stubs, ex-coupon accrual, two faces, inclusion policy and frozen live inputs. Independent closed-form zero-coupon checks supplement native prices. Generated stubs, enum integer/member accounting and unchanged static-typing diagnostics are required.
The yield and Z-spread routes above do not add ATM-rate helpers or an InterestRate facade. Ordinary duration, convexity and BPV/YVBP are documented below; callable-bond analytics remain separate. No core algorithm, version or existing engine-based API is changed.
Ordinary bond risk¶
These additive methods expose existing BondFunctions math for ordinary fixed,
zero and floating-rate bonds. They do not require a pricing engine, attach an
engine, change cached engine state or replace callable-bond effective risk.
Callable backing is rejected explicitly, even though it inherits Bond methods.
Python Bond method |
Go *Bond method |
Meaning |
|---|---|---|
duration |
Duration |
Simple, Macaulay or Modified duration |
convexity |
Convexity |
Flat-yield second derivative divided by value |
basis_point_value |
BasisPointValue |
Signed currency value for a +1 bp yield change |
yield_value_basis_point |
YieldValueBasisPoint |
Yield change for a +0.01 currency value change |
Python takes (yield_rate, day_counter, compounding, frequency, *, settlement=None).
duration additionally takes keyword-only duration_type=Duration.Modified, with
Duration imported from itofin.instruments. Go takes (rate, BondYieldConfig);
Duration takes (rate, DurationType, BondYieldConfig), using SimpleDuration,
MacaulayDuration or ModifiedDuration. Macaulay requires Compounded, not a
hybrid or continuous convention. Each metric is queried independently, so an
unsupported Macaulay query does not prevent other valid risk queries.
The C scalar query is itofin_bond_risk with the existing yield config. Its
metric fields are 0 Simple, 1 Macaulay, 2 Modified, 3 convexity, 4 BPV, 5 YVBP.
Errors leave the output untouched. The usual thread/context/handle contract applies.
BPV and YVBP are not per-100 quotes. They use actual currency cash flows:
BPV scales with face amount and YVBP inversely with it. For a paying bond they
are normally negative. The native BPV convention retains the convexity correction
0.5 * (convexity / 100) * value * 0.0001**2; no rescaling or new math is added.
Duration and convexity are independent of face scaling.
Explicit settlement is passed unchanged; omission retains existing checked lag
and issue-date-floor behavior. Settlement-date and ex-coupon flows are excluded
according to native analytical conventions. Nontradable bonds fail. Floating
queries retain current fixing, forecast and coupon-pricer guards, even while an
engine cache is frozen. Live retained market inputs are read for analytical risk;
freeze() does not turn risk into a historical snapshot.
from itofin.instruments import Duration
modified = bond.duration(
0.04, DayCounter.actual365_fixed(), Compounding.Compounded,
Frequency.Semiannual, duration_type=Duration.Modified, settlement=settlement,
)
bpv = bond.basis_point_value(
0.04, DayCounter.actual365_fixed(), Compounding.Compounded,
Frequency.Semiannual, settlement=settlement,
)
The shared 55-case test fixture was freshly generated from a QuantLib 1.43 wheel
on the existing analytics construction inputs. Source review uses QuantLib
9863b578af0caa4cecabf697196533e84a8308b6 (1.43-dev), not a claim that this source
pin produced the wheel. Matching distribution notices retain upstream attribution.
Coupon-rate BPS and default own-NPV ATM are documented separately in
bond coupon analytics.