Skip to content

Derivatives

Delta-1 instruments on indices, ETFs and equities: index and ETF futures, total return swaps, rate curves, term structures, and the pricing functions behind them.

derivatives

The 'derivatives' package models exchange-traded and OTC Delta-1 derivatives that reference beacon indices, ETFs, and equities.

DerivativeBase

DerivativeBase(
    derivative_id: str,
    underlying_id: str,
    underlying_type: str,
    currency: str,
    expiry_date: str,
    notional: float,
)

Bases: ABC

Abstract base for Delta-1 derivative instruments.

Holds the common contract terms (identifiers, currency, expiry, notional) and the ACT/365 time-to-expiry helper. Concrete subclasses implement :meth:fair_value and :meth:mark_to_market.

Initialise the common contract terms.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for this derivative.

required
underlying_id str

Identifier of the referenced underlying.

required
underlying_type str

One of INDEX, ETF or EQUITY (case-insensitive).

required
currency str

Contract currency (e.g. USD).

required
expiry_date str

Expiry date (YYYY-MM-DD).

required
notional float

Contract notional; must be positive.

required

Raises:

Type Description
ValueError

If any required field is empty/invalid, the underlying type is unrecognised, or notional is not positive.

time_to_expiry

time_to_expiry(valuation_date: Timestamp) -> float

Time to expiry in years using the ACT/365 convention.

Parameters:

Name Type Description Default
valuation_date Timestamp

The date from which to measure.

required

Returns:

Type Description
float

Years to expiry, clamped to 0.0 once the contract has expired.

fair_value abstractmethod

fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Return the model fair value of the derivative.

Parameters:

Name Type Description Default
spot_price float

Current spot/level of the underlying.

required
valuation_date Timestamp

The valuation date.

required
market_data dict[str, Any]

Additional inputs keyed by name. Most are scalar rates (e.g. risk_free_rate, dividend_yield), but subclasses also read non-scalar entries such as discrete_dividends (a list of (time, amount) tuples).

required

Returns:

Type Description
float

The fair value in contract currency.

mark_to_market abstractmethod

mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Mark the position to market against an observed market_price.

Parameters:

Name Type Description Default
market_price float

Observed traded price of the derivative.

required
spot_price float

Current spot/level of the underlying.

required
valuation_date Timestamp

The valuation date.

required
market_data dict[str, Any]

Additional inputs keyed by name.

required

Returns:

Type Description
dict[str, float]

A dictionary of mark-to-market results (e.g. fair value, PnL,

dict[str, float]

basis) in contract currency.

RateCurve dataclass

RateCurve(
    tenors: tuple[float, ...], rates: tuple[float, ...]
)

A zero-rate curve defined by pillar points.

Construction raises CalculationError if there are no pillars, the tenor and rate counts differ, a tenor is negative, or the tenors are not strictly increasing. :meth:from_pillars sorts them for you.

Attributes:

Name Type Description
tenors tuple[float, ...]

Pillar tenors in years, strictly increasing.

rates tuple[float, ...]

Continuously compounded zero rate at each pillar.

is_flat property

is_flat: bool

Whether every pillar carries the same rate.

flat classmethod

flat(rate: float) -> RateCurve

A curve with the same rate at every tenor.

The bridge back to scalar-rate pricing: a flat curve returns exactly the rate it was given at every tenor, so pricing off it reproduces scalar-rate pricing bit for bit rather than approximately.

Parameters:

Name Type Description Default
rate float

The continuously compounded rate.

required

Returns:

Name Type Description
RateCurve RateCurve

A single-pillar curve.

from_pillars classmethod

from_pillars(pillars: dict[float, float]) -> RateCurve

Build a curve from a {tenor: rate} mapping, sorted by tenor.

Parameters:

Name Type Description Default
pillars dict[float, float]

Tenor in years to continuously compounded zero rate.

required

Returns:

Name Type Description
RateCurve RateCurve

The curve.

Raises:

Type Description
CalculationError

If pillars is empty, a tenor is negative, or two tenors are equal.

zero_rate

zero_rate(tenor: float) -> float

The zero rate at tenor, interpolated between pillars.

Parameters:

Name Type Description Default
tenor float

Years from the valuation date. Must be non-negative.

required

Returns:

Name Type Description
float float

The continuously compounded zero rate. Flat beyond the first

float

and last pillar.

Raises:

Type Description
CalculationError

If tenor is negative.

discount_factor

discount_factor(tenor: float) -> float

Present value of one unit paid at tenor.

Parameters:

Name Type Description Default
tenor float

Years from the valuation date.

required

Returns:

Name Type Description
float float

exp(-z(T) * T). Exactly 1.0 at tenor zero.

forward_rate

forward_rate(start: float, end: float) -> float

The rate implied between two future dates.

The rate that makes discounting to end the same as discounting to start and then forward at this rate, which is what a financing leg resetting at start should be projected at.

Parameters:

Name Type Description Default
start float

Start of the forward period, in years.

required
end float

End of the forward period, in years.

required

Returns:

Name Type Description
float float

Continuously compounded forward rate.

Raises:

Type Description
CalculationError

If end is not after start.

shifted

shifted(bump: float) -> RateCurve

A copy with every pillar moved by bump.

The parallel shift a DV01 is measured against.

Parameters:

Name Type Description Default
bump float

Amount to add to every rate, in decimal. One basis point is BASIS_POINT.

required

Returns:

Name Type Description
RateCurve RateCurve

The shifted curve. The original is unchanged.

with_pillar_bump

with_pillar_bump(tenor: float, bump: float) -> RateCurve

A copy with one pillar moved, for a key-rate sensitivity.

Parameters:

Name Type Description Default
tenor float

The pillar to move. Must be an existing pillar: bumping a tenor that is not there would silently add a pillar and change the curve's shape rather than its level, which is not what a key-rate bump means.

required
bump float

Amount to add to that pillar's rate.

required

Returns:

Name Type Description
RateCurve RateCurve

The bumped curve.

Raises:

Type Description
CalculationError

If tenor is not a pillar.

to_dict

to_dict() -> dict[float, float]

The pillars as a {tenor: rate} mapping.

ETFFuture

ETFFuture(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    expiry_date: str,
    contract_multiplier: float,
    tick_size: float,
    tick_value: float,
)

Bases: IndexFuture

A futures contract on an ETF.

Behaves like :class:IndexFuture but prices with discrete known dividends, which better reflects an ETF's periodic cash distributions than a continuous yield. When discrete dividends are supplied via the market_data key "discrete_dividends" (a list of (time_to_ex_years, amount) tuples for ex-dates within the tenor), fair value uses F = (S - PV(divs)) * exp(r * T). Otherwise it falls back to the continuous cost-of-carry model inherited from :class:IndexFuture.

Initialise an ETF future. See :class:IndexFuture for the args.

fair_value

fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Discrete-dividend fair value, falling back to continuous carry.

If market_data["discrete_dividends"] is present and non-empty, prices with the discrete-dividend model; otherwise defers to the continuous cost-of-carry model of :class:IndexFuture.

IndexFuture

IndexFuture(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    expiry_date: str,
    contract_multiplier: float,
    tick_size: float,
    tick_value: float,
    underlying_type: str = "INDEX",
)

Bases: DerivativeBase

A cash-settled futures contract on an equity index.

Prices are quoted in index points; currency amounts are obtained by multiplying by :attr:contract_multiplier. Fair value uses the cost-of-carry model from :mod:beacon.derivatives.pricing.

Market-data inputs (passed via the market_data dict on valuation methods) are read by key:

  • risk_free_rate: continuous risk-free rate r (default 0)
  • dividend_yield: continuous dividend yield q (default 0)
  • borrow_cost: continuous borrow/financing spread c (default 0)

Initialise an index future.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for the contract.

required
underlying_id str

Identifier of the referenced index.

required
currency str

Contract currency (e.g. USD).

required
expiry_date str

Expiry date (YYYY-MM-DD).

required
contract_multiplier float

Currency value of one index point.

required
tick_size float

Minimum price increment, in index points.

required
tick_value float

Currency value of one tick.

required
underlying_type str

Underlying instrument type; defaults to INDEX. Subclasses (e.g. :class:ETFFuture) override it.

'INDEX'

Raises:

Type Description
ValueError

If any of contract_multiplier, tick_size or tick_value is non-positive (plus the base-class validations).

fair_value

fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Cost-of-carry fair value F = S * exp((r - q + c) * T) in points.

Returns spot_price when the contract is at or past expiry (T == 0).

basis

basis(futures_price: float, spot_price: float) -> float

Simple basis: futures_price - spot_price (index points).

annualised_basis

annualised_basis(
    futures_price: float,
    spot_price: float,
    valuation_date: Timestamp,
) -> float

Annualised implied financing rate ln(F / S) / T.

Implemented via :func:implied_repo_rate with zero dividend yield.

Raises:

Type Description
ValueError

At or past expiry (T == 0), where the rate is undefined.

daily_settlement_pnl

daily_settlement_pnl(
    settle_today: float,
    settle_yesterday: float,
    contracts: float = 1.0,
) -> float

Variation-margin P&L for the day, in contract currency.

(settle_today - settle_yesterday) * contract_multiplier * contracts. Positive contracts is a long position, negative is short.

roll_cost

roll_cost(front_price: float, back_price: float) -> float

Cost of rolling from the front to the back contract: back - front.

Positive in contango (back above front), negative in backwardation.

mark_to_market

mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Mark the contract against an observed market_price.

Returns a dict with fair_value (points), basis (market vs spot), theoretical_edge (fair value minus market price), and time_to_expiry (years).

TotalReturnSwap

TotalReturnSwap(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    start_date: str,
    end_date: str,
    notional: float,
    spread_bps: float,
    reference_rate: str,
    payment_frequency: str,
    reset_type: str = "UNFUNDED",
    underlying_type: str = "INDEX",
)

Bases: DerivativeBase

A total return swap (TRS) on an index or equity basket.

The total-return receiver earns the price return of the underlying and pays a financing leg. For an UNFUNDED swap the financing leg is reference_rate + spread; for a FUNDED swap the principal is posted up front and only the spread accrues.

market_data inputs (read by key on the valuation methods):

  • initial_price: reference price S_0 at inception/last reset (defaults to spot_price, i.e. zero return)
  • reference_rate: the floating rate for the current period (default 0)
  • last_reset_date: start of the current accrual period (defaults to the swap start date)
  • dividends: what the underlying paid per unit since S_0, in its own points (default 0). They join the total return leg, so a price level plus its dividends values the swap as a total return level would. Leave them out when the level already counts them.

An expected dividend yield plays no part: the value is what has accrued since the last reset, so it counts the dividends actually paid.

The underlying is an index by default; pass underlying_type for an ETF or an equity basket. Financing accrues ACT/360.

Initialise a total return swap.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for the swap.

required
underlying_id str

Identifier of the referenced index/basket.

required
currency str

Contract currency.

required
start_date str

Swap start date (YYYY-MM-DD).

required
end_date str

Swap maturity date (YYYY-MM-DD); used as the base expiry.

required
notional float

Swap notional; must be positive.

required
spread_bps float

Financing spread over the reference rate, in basis points.

required
reference_rate str

Name/identifier of the floating reference rate (e.g. SOFR).

required
payment_frequency str

One of MONTHLY, QUARTERLY, SEMI-ANNUAL, ANNUAL (case-insensitive).

required
reset_type str

UNFUNDED (default) or FUNDED.

'UNFUNDED'
underlying_type str

What underlying_id refers to: INDEX (the default), ETF or EQUITY.

'INDEX'

Raises:

Type Description
ValueError

On empty dates, end_date not after start_date, unrecognised payment_frequency/reset_type, or the base-class validations (including non-positive notional).

financing_cost

financing_cost(
    valuation_date: Timestamp,
    last_reset_date: Timestamp,
    reference_rate: float,
) -> float

Financing accrued since last_reset_date on an ACT/360 basis.

For an UNFUNDED swap the accrual rate is reference_rate + spread; for a FUNDED swap only the spread accrues.

Parameters:

Name Type Description Default
valuation_date Timestamp

The accrual end date.

required
last_reset_date Timestamp

Start of the current accrual period.

required
reference_rate float

Floating reference rate for the period (decimal).

required

Returns:

Type Description
float

The accrued financing cost in contract currency.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

dv01

dv01(
    valuation_date: Timestamp,
    last_reset_date: Timestamp,
    reference_rate: float = 0.0,
) -> float

Change in the receiver's value for a one-basis-point rate rise.

Computed by bumping the reference rate by one basis point and revaluing the accrued financing, rather than by the closed form. The two agree exactly here, since financing is linear in the rate (-notional * 1bp * financing_duration).

The sign is negative for a total-return receiver, and that is not a convention choice. The receiver pays financing, so a higher rate makes their position worth less. Reporting DV01 as a positive magnitude is common, but it loses the one piece of information a risk report most needs: which way this position hurts.

A FUNDED swap returns 0.0. Only the spread accrues on one, and the spread does not move with the reference rate, so the position genuinely has no sensitivity to it, rather than a small one.

Parameters:

Name Type Description Default
valuation_date Timestamp

The accrual end date.

required
last_reset_date Timestamp

Start of the current accrual period.

required
reference_rate float

The floating rate the bump is applied to. The answer does not depend on its level, since financing is linear, but it is accepted so the call reads the same as the others.

0.0

Returns:

Name Type Description
float float

Value change per +1bp, in contract currency. Negative for a

float

receiver on an unfunded swap.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

financing_duration

financing_duration(
    valuation_date: Timestamp, last_reset_date: Timestamp
) -> float

The accrual year fraction the DV01 scales with, ACT/360.

Exposed because it is the whole of the DV01 story: the sensitivity is notional × 1bp × this, so a reader who wants to check the number by hand needs it rather than having to rederive the day count.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

fair_value

fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Total-return-receiver P&L: total return leg minus accrued financing.

receiver_pnl = notional * ((S_t + D) / S_0 - 1) - accrued_financing, with S_0, the dividends paid D, the reference rate and the last reset date read from market_data as described on the class.

Raises:

Type Description
ValueError

If initial_price is not positive, or valuation_date precedes the last reset date.

total_return_leg

total_return_leg(
    spot_price: float,
    initial_price: float,
    dividends: float = 0.0,
) -> float

What the total return leg has earned since initial_price: the level's change plus the dividends paid per unit, on the notional.

Raises:

Type Description
ValueError

If dividends is negative.

mark_to_market

mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Decompose the swap P&L into its legs.

market_price is unused (a TRS has no separately quoted price); it is accepted to satisfy the :class:DerivativeBase interface.

Returns a dict with total_return_leg, financing_leg, net_mtm and accrued_days.

FuturesQuote dataclass

FuturesQuote(
    expiry: Timestamp,
    market_price: float | None = None,
    label: str = "",
)

One expiry and the price the market puts on it.

Attributes:

Name Type Description
expiry Timestamp

Contract expiry date.

market_price float | None

Traded price. None when only a theoretical value is wanted, in which case basis and implied repo are not reported for this pillar rather than being invented.

label str

Optional contract code, for display.

TermStructure dataclass

TermStructure(
    underlying: str,
    spot: float,
    valuation_date: Timestamp,
    quotes: list[FuturesQuote],
    curve: RateCurve,
    dividend_yield: float = 0.0,
    borrow_cost: float = 0.0,
    _sorted: list[FuturesQuote] = list(),
)

A strip of futures on one underlying, valued off one curve.

Fair values use continuous cost of carry, with each expiry financed at the curve's zero rate for its ACT/365 time to expiry. Construction raises CalculationError if there are no quotes, spot is not positive, or an expiry is before valuation_date.

Attributes:

Name Type Description
underlying str

Identifier of the underlying.

spot float

Spot price at valuation_date.

valuation_date Timestamp

The date everything is measured from.

quotes list[FuturesQuote]

The expiries, in any order; they are sorted on construction.

curve RateCurve

Financing curve. A flat curve reproduces scalar-rate pricing exactly.

dividend_yield float

Continuous dividend yield on the underlying.

borrow_cost float

Continuous borrow spread.

expiries property

expiries: list[Timestamp]

Expiry dates, nearest first.

times_to_expiry

times_to_expiry() -> list[float]

Year fractions to each expiry, ACT/365.

financing_rates

financing_rates() -> list[float]

The curve's rate at each expiry.

theoretical_prices

theoretical_prices() -> pd.Series

Fair value at each expiry, off the curve.

Returns:

Type Description
Series

pd.Series: Indexed by expiry date.

market_prices

market_prices() -> pd.Series

Quoted prices, NaN where a quote carries none.

basis

basis() -> pd.Series

Market minus theoretical, per expiry.

Positive means the contract trades rich to the model.

implied_repo

implied_repo() -> pd.Series

The financing rate each quoted price implies.

(ln(F / S) + q * T) / T per expiry, using the strip's dividend yield. The borrow cost is not subtracted, so it shows up inside the implied rate.

NaN for expiries with no quote, and for an expiry today: a zero year fraction carries no information about a rate, and dividing by it would manufacture one.

Returns:

Type Description
Series

pd.Series: Continuously compounded rates, indexed by expiry.

to_frame

to_frame() -> pd.DataFrame

Everything the strip says, one row per expiry.

cost_of_carry_fair_value

cost_of_carry_fair_value(
    spot: float,
    risk_free_rate: float,
    dividend_yield: float,
    time_to_expiry_years: float,
    borrow_cost: float = 0.0,
) -> float

Fair forward/futures value under continuous cost of carry.

F = S * exp((r - q + c) * T)

Parameters:

Name Type Description Default
spot float

Current spot price S (must be non-negative).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
dividend_yield float

Continuous dividend yield q.

required
time_to_expiry_years float

Time to expiry T in years (must be non-negative).

required
borrow_cost float

Continuous borrow/financing spread c (default 0).

0.0

Returns:

Type Description
float

The fair value F. Equals spot when T == 0.

Raises:

Type Description
ValueError

If spot or time_to_expiry_years is negative.

discrete_dividend_fair_value

discrete_dividend_fair_value(
    spot: float,
    risk_free_rate: float,
    time_to_expiry_years: float,
    dividends: list[tuple[float, float]],
) -> float

Fair forward/futures value with discrete cash dividends.

F = (S - PV(divs)) * exp(r * T) where each dividend is discounted at the risk-free rate to today: PV = amount * exp(-r * t_ex).

Parameters:

Name Type Description Default
spot float

Current spot price S (must be non-negative).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
time_to_expiry_years float

Time to expiry T in years (must be non-negative).

required
dividends list[tuple[float, float]]

List of (time_to_ex_years, amount) tuples. Only dividends with ex-dates on or before expiry (0 <= t_ex <= T) are included.

required

Returns:

Type Description
float

The fair value F.

Raises:

Type Description
ValueError

If spot or time_to_expiry_years is negative.

futures_roll_return

futures_roll_return(
    front_price: float,
    back_price: float,
    front_expiry: Timestamp,
    back_expiry: Timestamp,
) -> float

Annualised simple roll return from rolling a front contract to a back one.

roll = (front / back - 1) / dt where dt is the ACT/365 year fraction between the two expiries. Positive in backwardation (front above back), negative in contango.

Parameters:

Name Type Description Default
front_price float

Price of the near (front) contract (must be positive).

required
back_price float

Price of the far (back) contract (must be positive).

required
front_expiry Timestamp

Expiry of the front contract.

required
back_expiry Timestamp

Expiry of the back contract (must be after front_expiry).

required

Returns:

Type Description
float

The annualised roll return as a decimal.

Raises:

Type Description
ValueError

If either price is non-positive, or back_expiry is not strictly after front_expiry.

implied_repo_rate

implied_repo_rate(
    futures_price: float,
    spot: float,
    dividend_yield: float,
    time_to_expiry_years: float,
) -> float

Continuously compounded financing rate implied by a futures price.

Inverts the cost-of-carry relationship: r_implied = (ln(F / S) + q * T) / T

Parameters:

Name Type Description Default
futures_price float

Observed futures price F (must be positive).

required
spot float

Current spot price S (must be positive).

required
dividend_yield float

Continuous dividend yield q.

required
time_to_expiry_years float

Time to expiry T in years (must be positive).

required

Returns:

Type Description
float

The implied repo (financing) rate.

Raises:

Type Description
ValueError

If time_to_expiry_years, spot or futures_price is non-positive.

trs_breakeven_spread

trs_breakeven_spread(
    futures_price: float,
    spot: float,
    risk_free_rate: float,
    time_to_expiry_years: float,
    dividend_yield: float,
) -> float

Financing spread at which a total return swap matches futures economics.

The futures price embeds an implied financing rate (:func:implied_repo_rate). A TRS financed at r + spread reproduces those economics when the spread equals the gap between the implied financing rate and the risk-free rate:

spread = implied_repo_rate(F, S, q, T) - r

A fairly priced future (financed exactly at r) gives a breakeven spread of zero.

Parameters:

Name Type Description Default
futures_price float

Observed futures price F (must be positive).

required
spot float

Current spot price S (must be positive).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
time_to_expiry_years float

Time to expiry T in years (must be positive).

required
dividend_yield float

Continuous dividend yield q.

required

Returns:

Type Description
float

The breakeven financing spread as a decimal.

Raises:

Type Description
ValueError

If time_to_expiry_years, spot or futures_price is non-positive.

sensitivity_grid

sensitivity_grid(
    spot: float,
    tenors: list[float],
    rates: list[float],
    dividend_yield: float = 0.0,
    borrow_cost: float = 0.0,
) -> pd.DataFrame

Fair value across a tenor × rate grid.

What a position is worth if the curve is somewhere else and expiry is further out: the two axes a Delta-1 desk actually moves along, laid out so the shape is visible at once rather than one revaluation at a time.

Parameters:

Name Type Description Default
spot float

Spot price of the underlying.

required
tenors list[float]

Times to expiry in years, one per row.

required
rates list[float]

Continuously compounded financing rates, one per column.

required
dividend_yield float

Continuous dividend yield.

0.0
borrow_cost float

Continuous borrow spread.

0.0

Returns:

Type Description
DataFrame

pd.DataFrame: Fair values, tenors on the index and rates on the

DataFrame

columns, both labelled with their values.

Raises:

Type Description
CalculationError

If either axis is empty.

base

DerivativeBase: abstract base class for all Delta-1 derivative instruments.

DerivativeBase

DerivativeBase(
    derivative_id: str,
    underlying_id: str,
    underlying_type: str,
    currency: str,
    expiry_date: str,
    notional: float,
)

Bases: ABC

Abstract base for Delta-1 derivative instruments.

Holds the common contract terms (identifiers, currency, expiry, notional) and the ACT/365 time-to-expiry helper. Concrete subclasses implement :meth:fair_value and :meth:mark_to_market.

Initialise the common contract terms.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for this derivative.

required
underlying_id str

Identifier of the referenced underlying.

required
underlying_type str

One of INDEX, ETF or EQUITY (case-insensitive).

required
currency str

Contract currency (e.g. USD).

required
expiry_date str

Expiry date (YYYY-MM-DD).

required
notional float

Contract notional; must be positive.

required

Raises:

Type Description
ValueError

If any required field is empty/invalid, the underlying type is unrecognised, or notional is not positive.

time_to_expiry
time_to_expiry(valuation_date: Timestamp) -> float

Time to expiry in years using the ACT/365 convention.

Parameters:

Name Type Description Default
valuation_date Timestamp

The date from which to measure.

required

Returns:

Type Description
float

Years to expiry, clamped to 0.0 once the contract has expired.

fair_value abstractmethod
fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Return the model fair value of the derivative.

Parameters:

Name Type Description Default
spot_price float

Current spot/level of the underlying.

required
valuation_date Timestamp

The valuation date.

required
market_data dict[str, Any]

Additional inputs keyed by name. Most are scalar rates (e.g. risk_free_rate, dividend_yield), but subclasses also read non-scalar entries such as discrete_dividends (a list of (time, amount) tuples).

required

Returns:

Type Description
float

The fair value in contract currency.

mark_to_market abstractmethod
mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Mark the position to market against an observed market_price.

Parameters:

Name Type Description Default
market_price float

Observed traded price of the derivative.

required
spot_price float

Current spot/level of the underlying.

required
valuation_date Timestamp

The valuation date.

required
market_data dict[str, Any]

Additional inputs keyed by name.

required

Returns:

Type Description
dict[str, float]

A dictionary of mark-to-market results (e.g. fair value, PnL,

dict[str, float]

basis) in contract currency.

curves

Interest-rate curves.

A curve is asked for a rate at a tenor, and the answer (a plain float) goes into a function from beacon.derivatives.pricing, which takes rates as scalars.

Rates are continuously compounded zero rates, matching what the pricing functions expect, so a flat curve and the scalar rate it replaces are the same number and produce the same answer to the last bit.

Interpolation, and what happens off the ends

Linear in the zero rate between pillars: the conventional choice, and the one whose failure modes are understood. Beyond the first and last pillar the curve is flat, holding the nearest pillar's rate rather than continuing its slope. Extrapolating a slope off the end of a curve is how a two-year rate becomes a negative thirty-year rate: it is arithmetically reasonable and financially nonsense, and the error appears far from the code that caused it.

RateCurve dataclass

RateCurve(
    tenors: tuple[float, ...], rates: tuple[float, ...]
)

A zero-rate curve defined by pillar points.

Construction raises CalculationError if there are no pillars, the tenor and rate counts differ, a tenor is negative, or the tenors are not strictly increasing. :meth:from_pillars sorts them for you.

Attributes:

Name Type Description
tenors tuple[float, ...]

Pillar tenors in years, strictly increasing.

rates tuple[float, ...]

Continuously compounded zero rate at each pillar.

is_flat property
is_flat: bool

Whether every pillar carries the same rate.

flat classmethod
flat(rate: float) -> RateCurve

A curve with the same rate at every tenor.

The bridge back to scalar-rate pricing: a flat curve returns exactly the rate it was given at every tenor, so pricing off it reproduces scalar-rate pricing bit for bit rather than approximately.

Parameters:

Name Type Description Default
rate float

The continuously compounded rate.

required

Returns:

Name Type Description
RateCurve RateCurve

A single-pillar curve.

from_pillars classmethod
from_pillars(pillars: dict[float, float]) -> RateCurve

Build a curve from a {tenor: rate} mapping, sorted by tenor.

Parameters:

Name Type Description Default
pillars dict[float, float]

Tenor in years to continuously compounded zero rate.

required

Returns:

Name Type Description
RateCurve RateCurve

The curve.

Raises:

Type Description
CalculationError

If pillars is empty, a tenor is negative, or two tenors are equal.

zero_rate
zero_rate(tenor: float) -> float

The zero rate at tenor, interpolated between pillars.

Parameters:

Name Type Description Default
tenor float

Years from the valuation date. Must be non-negative.

required

Returns:

Name Type Description
float float

The continuously compounded zero rate. Flat beyond the first

float

and last pillar.

Raises:

Type Description
CalculationError

If tenor is negative.

discount_factor
discount_factor(tenor: float) -> float

Present value of one unit paid at tenor.

Parameters:

Name Type Description Default
tenor float

Years from the valuation date.

required

Returns:

Name Type Description
float float

exp(-z(T) * T). Exactly 1.0 at tenor zero.

forward_rate
forward_rate(start: float, end: float) -> float

The rate implied between two future dates.

The rate that makes discounting to end the same as discounting to start and then forward at this rate, which is what a financing leg resetting at start should be projected at.

Parameters:

Name Type Description Default
start float

Start of the forward period, in years.

required
end float

End of the forward period, in years.

required

Returns:

Name Type Description
float float

Continuously compounded forward rate.

Raises:

Type Description
CalculationError

If end is not after start.

shifted
shifted(bump: float) -> RateCurve

A copy with every pillar moved by bump.

The parallel shift a DV01 is measured against.

Parameters:

Name Type Description Default
bump float

Amount to add to every rate, in decimal. One basis point is BASIS_POINT.

required

Returns:

Name Type Description
RateCurve RateCurve

The shifted curve. The original is unchanged.

with_pillar_bump
with_pillar_bump(tenor: float, bump: float) -> RateCurve

A copy with one pillar moved, for a key-rate sensitivity.

Parameters:

Name Type Description Default
tenor float

The pillar to move. Must be an existing pillar: bumping a tenor that is not there would silently add a pillar and change the curve's shape rather than its level, which is not what a key-rate bump means.

required
bump float

Amount to add to that pillar's rate.

required

Returns:

Name Type Description
RateCurve RateCurve

The bumped curve.

Raises:

Type Description
CalculationError

If tenor is not a pillar.

to_dict
to_dict() -> dict[float, float]

The pillars as a {tenor: rate} mapping.

forwards

Forward contracts referencing beacon instruments.

A placeholder: nothing is implemented here yet. An EquityForward is planned.

futures

Futures contracts referencing beacon instruments: IndexFuture (on an index) and ETFFuture (on an ETF).

IndexFuture

IndexFuture(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    expiry_date: str,
    contract_multiplier: float,
    tick_size: float,
    tick_value: float,
    underlying_type: str = "INDEX",
)

Bases: DerivativeBase

A cash-settled futures contract on an equity index.

Prices are quoted in index points; currency amounts are obtained by multiplying by :attr:contract_multiplier. Fair value uses the cost-of-carry model from :mod:beacon.derivatives.pricing.

Market-data inputs (passed via the market_data dict on valuation methods) are read by key:

  • risk_free_rate: continuous risk-free rate r (default 0)
  • dividend_yield: continuous dividend yield q (default 0)
  • borrow_cost: continuous borrow/financing spread c (default 0)

Initialise an index future.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for the contract.

required
underlying_id str

Identifier of the referenced index.

required
currency str

Contract currency (e.g. USD).

required
expiry_date str

Expiry date (YYYY-MM-DD).

required
contract_multiplier float

Currency value of one index point.

required
tick_size float

Minimum price increment, in index points.

required
tick_value float

Currency value of one tick.

required
underlying_type str

Underlying instrument type; defaults to INDEX. Subclasses (e.g. :class:ETFFuture) override it.

'INDEX'

Raises:

Type Description
ValueError

If any of contract_multiplier, tick_size or tick_value is non-positive (plus the base-class validations).

fair_value
fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Cost-of-carry fair value F = S * exp((r - q + c) * T) in points.

Returns spot_price when the contract is at or past expiry (T == 0).

basis
basis(futures_price: float, spot_price: float) -> float

Simple basis: futures_price - spot_price (index points).

annualised_basis
annualised_basis(
    futures_price: float,
    spot_price: float,
    valuation_date: Timestamp,
) -> float

Annualised implied financing rate ln(F / S) / T.

Implemented via :func:implied_repo_rate with zero dividend yield.

Raises:

Type Description
ValueError

At or past expiry (T == 0), where the rate is undefined.

daily_settlement_pnl
daily_settlement_pnl(
    settle_today: float,
    settle_yesterday: float,
    contracts: float = 1.0,
) -> float

Variation-margin P&L for the day, in contract currency.

(settle_today - settle_yesterday) * contract_multiplier * contracts. Positive contracts is a long position, negative is short.

roll_cost
roll_cost(front_price: float, back_price: float) -> float

Cost of rolling from the front to the back contract: back - front.

Positive in contango (back above front), negative in backwardation.

mark_to_market
mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Mark the contract against an observed market_price.

Returns a dict with fair_value (points), basis (market vs spot), theoretical_edge (fair value minus market price), and time_to_expiry (years).

ETFFuture

ETFFuture(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    expiry_date: str,
    contract_multiplier: float,
    tick_size: float,
    tick_value: float,
)

Bases: IndexFuture

A futures contract on an ETF.

Behaves like :class:IndexFuture but prices with discrete known dividends, which better reflects an ETF's periodic cash distributions than a continuous yield. When discrete dividends are supplied via the market_data key "discrete_dividends" (a list of (time_to_ex_years, amount) tuples for ex-dates within the tenor), fair value uses F = (S - PV(divs)) * exp(r * T). Otherwise it falls back to the continuous cost-of-carry model inherited from :class:IndexFuture.

Initialise an ETF future. See :class:IndexFuture for the args.

fair_value
fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Discrete-dividend fair value, falling back to continuous carry.

If market_data["discrete_dividends"] is present and non-empty, prices with the discrete-dividend model; otherwise defers to the continuous cost-of-carry model of :class:IndexFuture.

pricing

Pure, stateless pricing functions for Delta-1 derivatives.

These functions implement standard textbook cost-of-carry relationships and have zero dependencies on the rest of Beacon (only the standard library and pandas Timestamps for date arithmetic). All rates are continuously compounded and expressed as annual decimals (e.g. 0.05 for 5%); times are in years.

cost_of_carry_fair_value

cost_of_carry_fair_value(
    spot: float,
    risk_free_rate: float,
    dividend_yield: float,
    time_to_expiry_years: float,
    borrow_cost: float = 0.0,
) -> float

Fair forward/futures value under continuous cost of carry.

F = S * exp((r - q + c) * T)

Parameters:

Name Type Description Default
spot float

Current spot price S (must be non-negative).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
dividend_yield float

Continuous dividend yield q.

required
time_to_expiry_years float

Time to expiry T in years (must be non-negative).

required
borrow_cost float

Continuous borrow/financing spread c (default 0).

0.0

Returns:

Type Description
float

The fair value F. Equals spot when T == 0.

Raises:

Type Description
ValueError

If spot or time_to_expiry_years is negative.

discrete_dividend_fair_value

discrete_dividend_fair_value(
    spot: float,
    risk_free_rate: float,
    time_to_expiry_years: float,
    dividends: list[tuple[float, float]],
) -> float

Fair forward/futures value with discrete cash dividends.

F = (S - PV(divs)) * exp(r * T) where each dividend is discounted at the risk-free rate to today: PV = amount * exp(-r * t_ex).

Parameters:

Name Type Description Default
spot float

Current spot price S (must be non-negative).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
time_to_expiry_years float

Time to expiry T in years (must be non-negative).

required
dividends list[tuple[float, float]]

List of (time_to_ex_years, amount) tuples. Only dividends with ex-dates on or before expiry (0 <= t_ex <= T) are included.

required

Returns:

Type Description
float

The fair value F.

Raises:

Type Description
ValueError

If spot or time_to_expiry_years is negative.

implied_repo_rate

implied_repo_rate(
    futures_price: float,
    spot: float,
    dividend_yield: float,
    time_to_expiry_years: float,
) -> float

Continuously compounded financing rate implied by a futures price.

Inverts the cost-of-carry relationship: r_implied = (ln(F / S) + q * T) / T

Parameters:

Name Type Description Default
futures_price float

Observed futures price F (must be positive).

required
spot float

Current spot price S (must be positive).

required
dividend_yield float

Continuous dividend yield q.

required
time_to_expiry_years float

Time to expiry T in years (must be positive).

required

Returns:

Type Description
float

The implied repo (financing) rate.

Raises:

Type Description
ValueError

If time_to_expiry_years, spot or futures_price is non-positive.

futures_roll_return

futures_roll_return(
    front_price: float,
    back_price: float,
    front_expiry: Timestamp,
    back_expiry: Timestamp,
) -> float

Annualised simple roll return from rolling a front contract to a back one.

roll = (front / back - 1) / dt where dt is the ACT/365 year fraction between the two expiries. Positive in backwardation (front above back), negative in contango.

Parameters:

Name Type Description Default
front_price float

Price of the near (front) contract (must be positive).

required
back_price float

Price of the far (back) contract (must be positive).

required
front_expiry Timestamp

Expiry of the front contract.

required
back_expiry Timestamp

Expiry of the back contract (must be after front_expiry).

required

Returns:

Type Description
float

The annualised roll return as a decimal.

Raises:

Type Description
ValueError

If either price is non-positive, or back_expiry is not strictly after front_expiry.

trs_breakeven_spread

trs_breakeven_spread(
    futures_price: float,
    spot: float,
    risk_free_rate: float,
    time_to_expiry_years: float,
    dividend_yield: float,
) -> float

Financing spread at which a total return swap matches futures economics.

The futures price embeds an implied financing rate (:func:implied_repo_rate). A TRS financed at r + spread reproduces those economics when the spread equals the gap between the implied financing rate and the risk-free rate:

spread = implied_repo_rate(F, S, q, T) - r

A fairly priced future (financed exactly at r) gives a breakeven spread of zero.

Parameters:

Name Type Description Default
futures_price float

Observed futures price F (must be positive).

required
spot float

Current spot price S (must be positive).

required
risk_free_rate float

Continuously compounded risk-free rate r.

required
time_to_expiry_years float

Time to expiry T in years (must be positive).

required
dividend_yield float

Continuous dividend yield q.

required

Returns:

Type Description
float

The breakeven financing spread as a decimal.

Raises:

Type Description
ValueError

If time_to_expiry_years, spot or futures_price is non-positive.

swaps

Swap contracts referencing beacon instruments: TotalReturnSwap.

TotalReturnSwap

TotalReturnSwap(
    derivative_id: str,
    underlying_id: str,
    currency: str,
    start_date: str,
    end_date: str,
    notional: float,
    spread_bps: float,
    reference_rate: str,
    payment_frequency: str,
    reset_type: str = "UNFUNDED",
    underlying_type: str = "INDEX",
)

Bases: DerivativeBase

A total return swap (TRS) on an index or equity basket.

The total-return receiver earns the price return of the underlying and pays a financing leg. For an UNFUNDED swap the financing leg is reference_rate + spread; for a FUNDED swap the principal is posted up front and only the spread accrues.

market_data inputs (read by key on the valuation methods):

  • initial_price: reference price S_0 at inception/last reset (defaults to spot_price, i.e. zero return)
  • reference_rate: the floating rate for the current period (default 0)
  • last_reset_date: start of the current accrual period (defaults to the swap start date)
  • dividends: what the underlying paid per unit since S_0, in its own points (default 0). They join the total return leg, so a price level plus its dividends values the swap as a total return level would. Leave them out when the level already counts them.

An expected dividend yield plays no part: the value is what has accrued since the last reset, so it counts the dividends actually paid.

The underlying is an index by default; pass underlying_type for an ETF or an equity basket. Financing accrues ACT/360.

Initialise a total return swap.

Parameters:

Name Type Description Default
derivative_id str

Unique identifier for the swap.

required
underlying_id str

Identifier of the referenced index/basket.

required
currency str

Contract currency.

required
start_date str

Swap start date (YYYY-MM-DD).

required
end_date str

Swap maturity date (YYYY-MM-DD); used as the base expiry.

required
notional float

Swap notional; must be positive.

required
spread_bps float

Financing spread over the reference rate, in basis points.

required
reference_rate str

Name/identifier of the floating reference rate (e.g. SOFR).

required
payment_frequency str

One of MONTHLY, QUARTERLY, SEMI-ANNUAL, ANNUAL (case-insensitive).

required
reset_type str

UNFUNDED (default) or FUNDED.

'UNFUNDED'
underlying_type str

What underlying_id refers to: INDEX (the default), ETF or EQUITY.

'INDEX'

Raises:

Type Description
ValueError

On empty dates, end_date not after start_date, unrecognised payment_frequency/reset_type, or the base-class validations (including non-positive notional).

financing_cost
financing_cost(
    valuation_date: Timestamp,
    last_reset_date: Timestamp,
    reference_rate: float,
) -> float

Financing accrued since last_reset_date on an ACT/360 basis.

For an UNFUNDED swap the accrual rate is reference_rate + spread; for a FUNDED swap only the spread accrues.

Parameters:

Name Type Description Default
valuation_date Timestamp

The accrual end date.

required
last_reset_date Timestamp

Start of the current accrual period.

required
reference_rate float

Floating reference rate for the period (decimal).

required

Returns:

Type Description
float

The accrued financing cost in contract currency.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

dv01
dv01(
    valuation_date: Timestamp,
    last_reset_date: Timestamp,
    reference_rate: float = 0.0,
) -> float

Change in the receiver's value for a one-basis-point rate rise.

Computed by bumping the reference rate by one basis point and revaluing the accrued financing, rather than by the closed form. The two agree exactly here, since financing is linear in the rate (-notional * 1bp * financing_duration).

The sign is negative for a total-return receiver, and that is not a convention choice. The receiver pays financing, so a higher rate makes their position worth less. Reporting DV01 as a positive magnitude is common, but it loses the one piece of information a risk report most needs: which way this position hurts.

A FUNDED swap returns 0.0. Only the spread accrues on one, and the spread does not move with the reference rate, so the position genuinely has no sensitivity to it, rather than a small one.

Parameters:

Name Type Description Default
valuation_date Timestamp

The accrual end date.

required
last_reset_date Timestamp

Start of the current accrual period.

required
reference_rate float

The floating rate the bump is applied to. The answer does not depend on its level, since financing is linear, but it is accepted so the call reads the same as the others.

0.0

Returns:

Name Type Description
float float

Value change per +1bp, in contract currency. Negative for a

float

receiver on an unfunded swap.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

financing_duration
financing_duration(
    valuation_date: Timestamp, last_reset_date: Timestamp
) -> float

The accrual year fraction the DV01 scales with, ACT/360.

Exposed because it is the whole of the DV01 story: the sensitivity is notional × 1bp × this, so a reader who wants to check the number by hand needs it rather than having to rederive the day count.

Raises:

Type Description
ValueError

If valuation_date precedes last_reset_date.

fair_value
fair_value(
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> float

Total-return-receiver P&L: total return leg minus accrued financing.

receiver_pnl = notional * ((S_t + D) / S_0 - 1) - accrued_financing, with S_0, the dividends paid D, the reference rate and the last reset date read from market_data as described on the class.

Raises:

Type Description
ValueError

If initial_price is not positive, or valuation_date precedes the last reset date.

total_return_leg
total_return_leg(
    spot_price: float,
    initial_price: float,
    dividends: float = 0.0,
) -> float

What the total return leg has earned since initial_price: the level's change plus the dividends paid per unit, on the notional.

Raises:

Type Description
ValueError

If dividends is negative.

mark_to_market
mark_to_market(
    market_price: float,
    spot_price: float,
    valuation_date: Timestamp,
    market_data: dict[str, Any],
) -> dict[str, float]

Decompose the swap P&L into its legs.

market_price is unused (a TRS has no separately quoted price); it is accepted to satisfy the :class:DerivativeBase interface.

Returns a dict with total_return_leg, financing_leg, net_mtm and accrued_days.

term_structure

Several expiries on one underlying, and what they say about each other.

A single futures price is not very informative. A strip of them is: the shape of the basis across expiries is where financing assumptions, dividend expectations and plain supply-and-demand show up, and none of that is visible one contract at a time.

Two views of the same disagreement, and it is worth being clear that they are the same disagreement:

  • Basis: market price minus theoretical price. Answers "how much is this contract off my model", in price terms.
  • Implied repo: the financing rate that would make the model agree with the market. Answers "what would I have to believe for this price to be right", in rate terms.

A rich contract has a positive basis and an implied repo above the curve (plus any borrow cost, which the implied repo absorbs). They cannot disagree about direction.

FuturesQuote dataclass

FuturesQuote(
    expiry: Timestamp,
    market_price: float | None = None,
    label: str = "",
)

One expiry and the price the market puts on it.

Attributes:

Name Type Description
expiry Timestamp

Contract expiry date.

market_price float | None

Traded price. None when only a theoretical value is wanted, in which case basis and implied repo are not reported for this pillar rather than being invented.

label str

Optional contract code, for display.

TermStructure dataclass

TermStructure(
    underlying: str,
    spot: float,
    valuation_date: Timestamp,
    quotes: list[FuturesQuote],
    curve: RateCurve,
    dividend_yield: float = 0.0,
    borrow_cost: float = 0.0,
    _sorted: list[FuturesQuote] = list(),
)

A strip of futures on one underlying, valued off one curve.

Fair values use continuous cost of carry, with each expiry financed at the curve's zero rate for its ACT/365 time to expiry. Construction raises CalculationError if there are no quotes, spot is not positive, or an expiry is before valuation_date.

Attributes:

Name Type Description
underlying str

Identifier of the underlying.

spot float

Spot price at valuation_date.

valuation_date Timestamp

The date everything is measured from.

quotes list[FuturesQuote]

The expiries, in any order; they are sorted on construction.

curve RateCurve

Financing curve. A flat curve reproduces scalar-rate pricing exactly.

dividend_yield float

Continuous dividend yield on the underlying.

borrow_cost float

Continuous borrow spread.

expiries property
expiries: list[Timestamp]

Expiry dates, nearest first.

times_to_expiry
times_to_expiry() -> list[float]

Year fractions to each expiry, ACT/365.

financing_rates
financing_rates() -> list[float]

The curve's rate at each expiry.

theoretical_prices
theoretical_prices() -> pd.Series

Fair value at each expiry, off the curve.

Returns:

Type Description
Series

pd.Series: Indexed by expiry date.

market_prices
market_prices() -> pd.Series

Quoted prices, NaN where a quote carries none.

basis
basis() -> pd.Series

Market minus theoretical, per expiry.

Positive means the contract trades rich to the model.

implied_repo
implied_repo() -> pd.Series

The financing rate each quoted price implies.

(ln(F / S) + q * T) / T per expiry, using the strip's dividend yield. The borrow cost is not subtracted, so it shows up inside the implied rate.

NaN for expiries with no quote, and for an expiry today: a zero year fraction carries no information about a rate, and dividing by it would manufacture one.

Returns:

Type Description
Series

pd.Series: Continuously compounded rates, indexed by expiry.

to_frame
to_frame() -> pd.DataFrame

Everything the strip says, one row per expiry.

sensitivity_grid

sensitivity_grid(
    spot: float,
    tenors: list[float],
    rates: list[float],
    dividend_yield: float = 0.0,
    borrow_cost: float = 0.0,
) -> pd.DataFrame

Fair value across a tenor × rate grid.

What a position is worth if the curve is somewhere else and expiry is further out: the two axes a Delta-1 desk actually moves along, laid out so the shape is visible at once rather than one revaluation at a time.

Parameters:

Name Type Description Default
spot float

Spot price of the underlying.

required
tenors list[float]

Times to expiry in years, one per row.

required
rates list[float]

Continuously compounded financing rates, one per column.

required
dividend_yield float

Continuous dividend yield.

0.0
borrow_cost float

Continuous borrow spread.

0.0

Returns:

Type Description
DataFrame

pd.DataFrame: Fair values, tenors on the index and rates on the

DataFrame

columns, both labelled with their values.

Raises:

Type Description
CalculationError

If either axis is empty.

valuation

A futures contract, a total return swap and a roll valued from their terms, with everything shown beside the price: the carry split into its parts, a sensitivity grid, a swap's financing schedule, its fair spread and its breakeven against futures.

The formulas are beacon.derivatives.pricing's; this module puts a curve's rates and a contract's dates into them. Carry is shown as the price effect of each rate, each priced with that rate alone. The parts do not sum to the total exactly, because carry compounds rather than adds; the residual is given rather than spread across them.

Carry dataclass

Carry(
    total: float,
    financing: float,
    dividend: float,
    borrow: float,
    residual: float,
)

A futures price's carry: its total, the price effect of each rate on its own, and what compounding leaves between them.

FuturesValuation dataclass

FuturesValuation(
    fair_value: float,
    time_to_expiry: float,
    financing_rate: float,
    carry: Carry,
    contract_value: float,
    basis: float | None,
    implied_repo: float | None,
    sensitivity: DataFrame,
)

A futures contract valued from its terms.

Attributes:

Name Type Description
fair_value float

Its theoretical price.

time_to_expiry float

In years.

financing_rate float

The curve's rate to expiry.

carry Carry

The carry, split by rate.

contract_value float

The fair value times the multiplier and contracts.

basis float | None

The market price less the fair value, when quoted.

implied_repo float | None

The financing rate the market price implies.

sensitivity DataFrame

Fair value across tenors (rows) and rates (columns).

Accrual dataclass

Accrual(
    start: Timestamp,
    end: Timestamp,
    days: int,
    rate: float,
    accrual_fraction: float,
    amount: float,
)

One financing period of a swap.

SwapValuation dataclass

SwapValuation(
    accrual_days: int,
    accrual_fraction: float,
    total_return_leg: float,
    financing_leg: float,
    present_value: float,
    dv01: float,
    fair_spread_bps: float | None,
    schedule: list[Accrual],
    breakeven: list[dict[str, float]] = list(),
)

A total return swap valued at a date.

Attributes:

Name Type Description
accrual_days int

Days since the last reset.

accrual_fraction float

Their ACT/360 year fraction.

total_return_leg float

What the underlying returned on the notional.

financing_leg float

The financing accrued since the last reset.

present_value float

The first less the second.

dv01 float

The financing leg's change for a basis point.

fair_spread_bps float | None

The spread at which the swap is worth nothing today; None when nothing has accrued.

schedule list[Accrual]

Each financing period from the last reset to maturity.

breakeven list[dict[str, float]]

The spread at which the swap matches each futures price.

Roll dataclass

Roll(
    front_price: float,
    back_price: float,
    roll_cost: float,
    annualised_roll: float,
)

Rolling from one contract to the next, both priced off the same spot and curve: the carry roll rather than a market one.

years_between

years_between(
    start: Timestamp,
    end: Timestamp,
    days_per_year: float = DAYS_PER_YEAR,
) -> float

The year fraction from start to end, by calendar days.

tenor_of

tenor_of(
    valuation_date: str | None,
    expiry: str | None,
    explicit: float | None,
) -> float

Year fraction to expiry, ACT/365, from two dates or as given.

Dates win when both are present: a tenor typed by hand can silently disagree with the expiry beside it.

Raises:

Type Description
InvalidArgumentError

If the expiry is before the valuation date, no tenor can be found, or the tenor is negative.

carry_of

carry_of(
    spot: float,
    rate: float,
    tenor: float,
    dividend_yield: float,
    borrow_cost: float,
    fair_value: float,
) -> Carry

The carry from spot to fair_value, split by rate.

value_future

value_future(
    spot: float,
    tenor: float,
    curve: RateCurve,
    dividend_yield: float = 0.0,
    borrow_cost: float = 0.0,
    dividends: list[tuple[float, float]] | None = None,
    market_price: float | None = None,
    contract_multiplier: float = 1.0,
    contracts: float = 1.0,
    grid_tenors: list[float] | None = None,
    grid_rates: list[float] | None = None,
) -> FuturesValuation

A futures contract on spot, tenor years out, financed on curve.

Parameters:

Name Type Description Default
spot float

The underlying's price.

required
tenor float

Years to expiry.

required
curve RateCurve

The financing curve.

required
dividend_yield float

A continuous yield, used without dividends.

0.0
borrow_cost float

The cost of borrowing the underlying.

0.0
dividends list[tuple[float, float]] | None

Discrete dividends as (years from now, amount); priced off them instead of the yield when given.

None
market_price float | None

A quote to measure the basis and implied repo from.

None
contract_multiplier float

Value per point.

1.0
contracts float

How many.

1.0
grid_tenors list[float] | None

The sensitivity grid's tenors; multiples of tenor when None.

None
grid_rates list[float] | None

Its rates; spreads around the curve's when None.

None

value_swap

value_swap(
    swap: TotalReturnSwap,
    valuation: Timestamp,
    last_reset: Timestamp,
    curve: RateCurve,
    fixed_rate: float,
    spot: float,
    initial_price: float,
    dividends: float = 0.0,
    futures_prices: list[float] | None = None,
    time_to_expiry: float | None = None,
    dividend_yield: float = 0.0,
) -> SwapValuation

swap valued at valuation, its current period fixed at fixed_rate since last_reset and the later ones projected off curve.

financing_schedule

financing_schedule(
    swap: TotalReturnSwap,
    valuation: Timestamp,
    last_reset: Timestamp,
    curve: RateCurve,
    fixed_rate: float,
) -> list[Accrual]

Each financing period from the last reset to maturity.

The current period accrues at the rate fixed at its reset; each later one at the curve's forward rate over it, so a flat curve reproduces the fixed rate and a shaped one prices later periods off the later curve.

roll_between

roll_between(
    spot: float,
    valuation: Timestamp,
    front_expiry: Timestamp,
    back_expiry: Timestamp,
    curve: RateCurve,
    dividend_yield: float = 0.0,
) -> Roll

The roll from the front_expiry contract to the back_expiry one.

Raises:

Type Description
InvalidArgumentError

If the back expiry is not after the front.