Fixed income: pricing a bond, solving for its yield, and the standard risk metrics (Macaulay and modified duration, convexity).
A bond pays a coupon of coupon_rate a year — split across freq payments
(semiannual by default) — and returns its face value at maturity, years
from now. Rates are quoted per year; ytm is the yield to maturity.
Settlement
These functions assume settlement falls on a coupon date, so the price is "clean" (no accrued interest) and the number of coupon periods is whole. Arbitrary settlement dates and day-count conventions are a later feature.
The risk metrics — duration/4, modified_duration/4, convexity/4 — don't
depend on the face value (it cancels out), so they leave it off and lead with
coupon_rate, unlike price/5 and ytm/5 which lead with face.
Summary
Functions
Convexity, in years² — the second-order sensitivity of a bond's price to yield. Pairs with modified duration to refine the price-change estimate.
Same as convexity/4, but returns the value directly and raises ArgumentError on error.
Macaulay duration — the present-value-weighted average time, in years, until a bond's cash flows are received. A plain measure of interest-rate sensitivity.
Same as duration/4, but returns the value directly and raises ArgumentError on error.
Modified duration — Macaulay duration divided by 1 + ytm/freq. It estimates
the percentage price change for a small change in yield.
Same as modified_duration/4, but returns the value directly and raises ArgumentError on error.
Prices a bond: the present value of its coupons and face value, discounted at
the yield ytm.
Same as price/5, but returns the value directly and raises ArgumentError on error.
Solves for a bond's yield to maturity — the annual yield that discounts its
coupons and face value back to price. The inverse of price/5.
Same as ytm/5, but returns the yield directly and raises ArgumentError on error.
Types
@type error() :: Finance.error()
@type option() :: Finance.option()
Functions
@spec convexity(number(), number(), number(), pos_integer(), [option()]) :: {:ok, float()} | {:error, error()}
Convexity, in years² — the second-order sensitivity of a bond's price to yield. Pairs with modified duration to refine the price-change estimate.
iex> Finance.Bonds.convexity(0.0, 0.05, 10, 1)
{:ok, 99.773243}
iex> Finance.Bonds.convexity(0.06, 0.06, 3, 1)
{:ok, 9.891032}
Same as convexity/4, but returns the value directly and raises ArgumentError on error.
@spec duration(number(), number(), number(), pos_integer(), [option()]) :: {:ok, float()} | {:error, error()}
Macaulay duration — the present-value-weighted average time, in years, until a bond's cash flows are received. A plain measure of interest-rate sensitivity.
The face value cancels out, so it isn't a parameter (see the module note).
iex> Finance.Bonds.duration(0.0, 0.05, 10, 1)
{:ok, 10.0}
iex> Finance.Bonds.duration(0.06, 0.06, 3, 1)
{:ok, 2.833393}
Same as duration/4, but returns the value directly and raises ArgumentError on error.
@spec modified_duration(number(), number(), number(), pos_integer(), [option()]) :: {:ok, float()} | {:error, error()}
Modified duration — Macaulay duration divided by 1 + ytm/freq. It estimates
the percentage price change for a small change in yield.
iex> Finance.Bonds.modified_duration(0.0, 0.05, 10, 1)
{:ok, 9.52381}
iex> Finance.Bonds.modified_duration(0.06, 0.06, 3, 1)
{:ok, 2.673012}
Same as modified_duration/4, but returns the value directly and raises ArgumentError on error.
@spec price(number(), number(), number(), number(), pos_integer(), [option()]) :: {:ok, float()} | {:error, error()}
Prices a bond: the present value of its coupons and face value, discounted at
the yield ytm.
When the coupon rate equals the yield, the bond prices at par.
iex> Finance.Bonds.price(100, 0.05, 0.05, 10)
{:ok, 100.0}
iex> Finance.Bonds.price(1000, 0.08, 0.10, 10)
{:ok, 875.377897}
Same as price/5, but returns the value directly and raises ArgumentError on error.
@spec ytm(number(), number(), number(), number(), pos_integer(), [option()]) :: {:ok, float()} | {:error, error()}
Solves for a bond's yield to maturity — the annual yield that discounts its
coupons and face value back to price. The inverse of price/5.
There is no closed form, so this reuses the same solver as
Finance.CashFlow.irr/1 and takes the same options; it returns
{:error, :did_not_converge} if no yield can be found.
iex> Finance.Bonds.ytm(1000, 0.08, 875.377897, 10)
{:ok, 0.1}
Same as ytm/5, but returns the yield directly and raises ArgumentError on error.