fuggers_py.funding

Public home for repo trades, repo quotes, haircut quotes, repo reference records, and funding analytics.

Use one-layer imports from fuggers_py.funding for funding objects and funding functions:

from fuggers_py.funding import RepoTrade, repo_net_carry

Date, Currency, and other shared core values come from fuggers_py.

What This Package Owns

funding owns:

  • repo trades: RepoTrade

  • repo and haircut quotes: RepoQuote and HaircutQuote

  • static repo reference records: RepoReferenceData

  • repo carry and financing helpers

  • implied repo and futures invoice helpers

  • haircut, all-in funding, and specialness helpers

Repo curves do not live here. Curve objects live under fuggers_py.curves. Bond futures contracts and conversion factors live under fuggers_py.rates. Bond collateral instruments live under fuggers_py.bonds.

Conventions

  • Rates are raw decimals. 0.045 means 4.5%.

  • Haircuts are raw decimals. 0.02 means 2%.

  • Currency cash amounts are plain currency units. 1000000 means one million.

  • Bond and futures prices are percent-of-par values. 99.5 means 99.5 per 100 face amount.

  • Accrued interest and coupon income in implied repo helpers are also percent-of-par values. 1.25 means 1.25 per 100 face amount, not 1.25 cash units.

  • face_amount is a currency amount.

  • Year fractions are raw decimals. 0.25 means one quarter of a year.

  • Repo trade dates are Date values.

  • Positive financing cost means a cost paid by the repo cash borrower.

  • Positive net carry means collateral income is larger than financing costs.

  • Positive specialness spread means the specific collateral repo rate is below the general collateral repo rate.

Public Exports

Export

What it is

RepoTrade

One repo trade with dates, rate, collateral price, haircut, and size.

RepoQuote

One market quote for a repo rate and optional haircut.

HaircutQuote

One market quote for a financing haircut.

RepoReferenceData

Static reference data for a repo instrument or repo market point.

repo_financing_cost

Repo interest paid over the trade period.

repo_net_carry

Collateral income minus repo and haircut funding costs.

repo_carry_return

Net carry divided by cash lent.

implied_repo_rate

Repo rate implied by buying a bond and delivering it into a future.

implied_repo_rate_from_trade

Same implied repo calculation, using dates and spot price from a RepoTrade.

futures_invoice_amount

Cash amount paid at futures delivery.

haircut_amount

Collateral value held back by a haircut.

financed_cash

Cash lent after the haircut.

haircut_financing_cost

Cost of funding the haircut amount.

haircut_drag

Extra cost of funding the haircut at a different rate than the repo rate.

all_in_financing_cost

Repo cost on financed cash plus haircut funding cost.

specialness_spread

General collateral rate minus specific collateral rate.

specialness_value

Currency value of the specialness spread.

is_special

True when the specific collateral rate is below the general collateral rate.

RepoTrade

RepoTrade represents one repo trade. A repo trade lends cash against collateral. The collateral is valued from a bond-style price, and the cash lent is reduced by any haircut.

Use RepoTrade when you want one object that can answer: how much cash is lent, how much collateral is behind the loan, how much interest is due, and what cash is due at repurchase.

Construction Behavior

RepoTrade stores:

  • start_date and end_date

  • rate

  • collateral_price

  • haircut

  • either notional or cash_amount

  • currency

  • day_count_convention

  • optional collateral_instrument_id

The constructor normalizes several inputs:

  • Decimal-like inputs may be Decimal, int, float, or str; they are stored as Decimal.

  • currency may be a Currency value or a currency string such as "usd".

  • collateral_instrument_id may be a string; whitespace is stripped.

  • day_count_convention may be a day-count value or a supported string alias. Supported aliases include ACT360, ACT_360, ACT365F, ACT_365_FIXED, ACT365L, ACT_365_LEAP, 30E_360, 30_360_E, and 30_360_US.

The constructor validates the trade:

  • end_date must be after start_date.

  • haircut must be at least 0 and below 1.

  • At least one of notional or cash_amount is required.

  • notional, when provided, must be positive.

  • cash_amount, when provided, must be positive.

Methods

day_count() returns the day-count object used by the trade.

year_fraction() returns the time between start_date and end_date as a decimal part of a year.

collateral_market_value() returns the market value of the collateral:

  • if notional is set: notional * collateral_price / 100

  • if only cash_amount is set: cash_amount / (1 - haircut)

haircut_amount() returns collateral_market_value() * haircut.

cash_lent() returns:

  • cash_amount, when the trade was built with a cash amount

  • otherwise collateral_market_value() * (1 - haircut)

interest_amount() returns cash_lent() * rate * year_fraction().

repurchase_amount() returns cash_lent() + interest_amount().

Example

from decimal import Decimal

from fuggers_py import Date
from fuggers_py.funding import RepoTrade, repo_financing_cost

trade = RepoTrade(
    start_date=Date.from_ymd(2026, 1, 2),
    end_date=Date.from_ymd(2026, 2, 1),
    rate="0.05",
    collateral_price="101.50",
    haircut="0.02",
    notional="1000000",
    currency="usd",
    day_count_convention="ACT_360",
    collateral_instrument_id="US91282CKH3",
)

year_fraction = trade.year_fraction()
collateral_value = trade.collateral_market_value()
cash_lent = trade.cash_lent()
haircut_cash = trade.haircut_amount()
repo_interest = trade.interest_amount()
cash_due_at_repurchase = trade.repurchase_amount()

same_interest = repo_financing_cost(trade)

assert collateral_value == Decimal("1015000")
assert cash_lent == Decimal("994700.00")
assert haircut_cash == Decimal("20300.00")
assert same_interest == repo_interest
assert cash_due_at_repurchase == cash_lent + repo_interest

RepoQuote

RepoQuote is a market quote for a repo rate. It can also carry a haircut, a start and end date, a tenor label such as "1W", a collateral type, an as-of date, a currency, a source, and bid/ask/mid quote sides.

Use it to store market data before feeding rates or haircuts into pricing, curve, or carry code.

Construction Behavior

The constructor normalizes values after the dataclass is created:

  • instrument_id is parsed and stripped.

  • rate, haircut, bid, ask, and mid are stored as Decimal when set.

  • term is stripped and uppercased.

  • collateral_type and source are stripped.

  • currency is stored as provided; pass a Currency value when you want a canonical currency object.

  • If mid is missing and rate is set, mid is set to rate.

  • If mid and rate are both missing, but both bid and ask are set, mid is set to the average of bid and ask.

Methods

quoted_value(side="mid") returns the requested quote side. The side may be "bid", "ask", or "mid". It returns None when that side is not available.

for_side(side) returns a copy of the quote with rate set to the requested side. It returns None when that side is not available.

Example

from decimal import Decimal

from fuggers_py import Currency, Date
from fuggers_py.funding import RepoQuote

quote = RepoQuote(
    instrument_id=" repo-ust-1w ",
    bid="0.0440",
    ask="0.0460",
    term=" 1w ",
    collateral_type=" UST ",
    as_of=Date.from_ymd(2026, 4, 17),
    currency=Currency.USD,
    source=" broker screen ",
)

mid_rate = quote.quoted_value("mid")
bid_rate = quote.quoted_value("bid")
ask_quote = quote.for_side("ask")

assert str(quote.instrument_id) == "repo-ust-1w"
assert quote.term == "1W"
assert quote.collateral_type == "UST"
assert mid_rate == Decimal("0.0450")
assert bid_rate == Decimal("0.0440")
assert ask_quote is not None
assert ask_quote.rate == Decimal("0.0460")

HaircutQuote

HaircutQuote is a market quote for a haircut. A haircut is the part of collateral value that is not financed. A 0.02 haircut means 2% of the collateral value is held back.

Use it when haircut data is separate from the repo rate quote.

Construction Behavior

The constructor normalizes values after the dataclass is created:

  • instrument_id is parsed and stripped.

  • haircut, bid, ask, and mid are stored as Decimal when set.

  • collateral_type and source are stripped.

  • currency is stored as provided; pass a Currency value when you want a canonical currency object.

  • If mid is missing and haircut is set, mid is set to haircut.

  • If mid and haircut are both missing, but both bid and ask are set, mid is set to the average of bid and ask.

Methods

quoted_value(side="mid") returns the requested quote side. The side may be "bid", "ask", or "mid". It returns None when that side is not available.

for_side(side) returns a copy of the quote with haircut set to the requested side. It returns None when that side is not available.

Example

from decimal import Decimal

from fuggers_py import Currency, Date
from fuggers_py.funding import HaircutQuote

quote = HaircutQuote(
    instrument_id=" ust-on-the-run ",
    bid="0.015",
    ask="0.025",
    collateral_type=" Treasury ",
    as_of=Date.from_ymd(2026, 4, 17),
    currency=Currency.USD,
)

mid_haircut = quote.quoted_value("mid")
ask_quote = quote.for_side("ask")

assert str(quote.instrument_id) == "ust-on-the-run"
assert quote.collateral_type == "Treasury"
assert mid_haircut == Decimal("0.020")
assert ask_quote is not None
assert ask_quote.haircut == Decimal("0.025")

RepoReferenceData

RepoReferenceData stores static repo facts. Static facts are values that describe the repo instrument or market point, not a live market quote.

Use it to keep the usual currency, collateral currency, term, collateral type, default haircut, and settlement lag for a repo market point.

Construction Behavior

The constructor normalizes values after the dataclass is created:

  • instrument_id is parsed and stripped.

  • term is stripped and uppercased.

  • collateral_type is stripped.

  • haircut is stored as Decimal when set.

  • currency and collateral_currency are stored as provided; pass Currency values when you want canonical currency objects.

RepoReferenceData has no helper methods beyond its public fields.

Example

from decimal import Decimal

from fuggers_py import Currency
from fuggers_py.funding import RepoReferenceData

reference = RepoReferenceData(
    instrument_id=" repo-ust-1w ",
    currency=Currency.USD,
    collateral_currency=Currency.USD,
    term=" 1w ",
    collateral_type=" UST ",
    haircut="0.02",
    settlement_lag_days=1,
)

assert str(reference.instrument_id) == "repo-ust-1w"
assert reference.term == "1W"
assert reference.collateral_type == "UST"
assert reference.haircut == Decimal("0.02")

Repo Carry Helpers

Repo carry helpers work from a RepoTrade.

repo_financing_cost(trade) returns trade.interest_amount(). The result is a currency amount.

repo_net_carry(trade, collateral_income=0, haircut_financing_cost=0) returns:

collateral_income - trade.interest_amount() - haircut_financing_cost

The result is a currency amount. Positive means income is larger than costs. Negative means the repo and haircut costs are larger than income.

repo_carry_return(trade, collateral_income=0, haircut_financing_cost=0) returns:

repo_net_carry(...) / trade.cash_lent()

The result is a raw decimal return. It raises ValueError if cash lent is zero.

Example

from decimal import Decimal

from fuggers_py import Date
from fuggers_py.funding import (
    RepoTrade,
    repo_carry_return,
    repo_financing_cost,
    repo_net_carry,
)

trade = RepoTrade(
    start_date=Date.from_ymd(2026, 2, 3),
    end_date=Date.from_ymd(2026, 3, 5),
    rate=Decimal("0.03"),
    collateral_price=Decimal("100.00"),
    haircut=Decimal("0.02"),
    cash_amount=Decimal("980000"),
    currency="USD",
)

financing_cost = repo_financing_cost(trade)
net_carry = repo_net_carry(
    trade,
    collateral_income=Decimal("5000"),
    haircut_financing_cost=Decimal("100"),
)
carry_return = repo_carry_return(
    trade,
    collateral_income=Decimal("5000"),
    haircut_financing_cost=Decimal("100"),
)

profitable = net_carry > Decimal("0")

assert financing_cost == trade.interest_amount()
assert net_carry == Decimal("5000") - financing_cost - Decimal("100")
assert carry_return == net_carry / trade.cash_lent()
assert profitable is True

Haircuts and All-In Financing Cost

These helpers use collateral value, haircut, rates, and year fraction directly. They do not need a RepoTrade.

haircut_amount(collateral_value, haircut) returns:

collateral_value * haircut

The result is a currency amount.

financed_cash(collateral_value, haircut) returns:

collateral_value - haircut_amount(...)

The result is the cash amount financed by the repo.

haircut_financing_cost(collateral_value, haircut, funding_rate, year_fraction) returns:

haircut_amount(...) * funding_rate * year_fraction

The result is a currency amount.

haircut_drag(collateral_value, haircut, repo_rate, haircut_funding_rate, year_fraction) returns:

haircut_amount(...) * (haircut_funding_rate - repo_rate) * year_fraction

Positive drag means the haircut is funded at a higher rate than the repo rate. Negative drag means the haircut is funded at a lower rate.

all_in_financing_cost(collateral_value, haircut, repo_rate, haircut_funding_rate, year_fraction) returns:

financed_cash(...) * repo_rate * year_fraction
+ haircut_financing_cost(...)

The result is a currency amount.

Example

from decimal import Decimal

from fuggers_py.funding import (
    all_in_financing_cost,
    financed_cash,
    haircut_amount,
    haircut_drag,
    haircut_financing_cost,
)

collateral_value = Decimal("1000000")
haircut = Decimal("0.02")
repo_rate = Decimal("0.045")
haircut_funding_rate = Decimal("0.055")
year_fraction = Decimal("0.25")

withheld_cash = haircut_amount(
    collateral_value=collateral_value,
    haircut=haircut,
)
cash_lent = financed_cash(
    collateral_value=collateral_value,
    haircut=haircut,
)
haircut_cost = haircut_financing_cost(
    collateral_value=collateral_value,
    haircut=haircut,
    funding_rate=haircut_funding_rate,
    year_fraction=year_fraction,
)
drag = haircut_drag(
    collateral_value=collateral_value,
    haircut=haircut,
    repo_rate=repo_rate,
    haircut_funding_rate=haircut_funding_rate,
    year_fraction=year_fraction,
)
all_in_cost = all_in_financing_cost(
    collateral_value=collateral_value,
    haircut=haircut,
    repo_rate=repo_rate,
    haircut_funding_rate=haircut_funding_rate,
    year_fraction=year_fraction,
)

assert withheld_cash == Decimal("20000.00")
assert cash_lent == Decimal("980000.00")
assert haircut_cost == Decimal("275.0000")
assert drag == Decimal("50.0000")
assert all_in_cost == Decimal("11300.0000")

Futures Invoice and Implied Repo

These helpers use bond-future delivery math.

futures_invoice_amount(futures_price, conversion_factor, accrued_at_delivery=0, face_amount=100) returns the cash paid at delivery:

face_amount * (futures_price * conversion_factor + accrued_at_delivery) / 100

futures_price and accrued_at_delivery are percent-of-par values. face_amount is a currency amount.

implied_repo_rate(...) calculates the repo rate implied by this trade:

  1. buy the bond at spot_price + accrued_on_purchase

  2. receive the futures invoice amount at delivery

  3. add coupon income received before delivery

  4. convert the gain or loss into an annualized raw decimal rate

In formula form:

purchase_amount = face_amount * (spot_price + accrued_on_purchase) / 100
delivery_amount = futures_invoice_amount(...) + face_amount * coupon_income / 100
implied_repo_rate = (delivery_amount / purchase_amount - 1) / year_fraction

spot_price, futures_price, coupon_income, accrued_on_purchase, and accrued_at_delivery are percent-of-par values. face_amount is a currency amount.

implied_repo_rate raises ValueError when the dates produce a zero year fraction or when the purchase amount is zero.

implied_repo_rate_from_trade(trade, ...) uses:

  • trade.collateral_price as the spot price

  • trade.start_date and trade.end_date as the implied repo dates

  • trade.day_count_convention as the day-count rule

  • trade.notional as face_amount when trade.notional is set

  • 100 as face_amount when the trade has no notional

It does not use trade.cash_amount as the futures face amount.

Example

from decimal import Decimal

from fuggers_py import Date
from fuggers_py.funding import (
    RepoTrade,
    futures_invoice_amount,
    implied_repo_rate,
    implied_repo_rate_from_trade,
)

start = Date.from_ymd(2026, 3, 1)
delivery = Date.from_ymd(2026, 6, 1)

invoice = futures_invoice_amount(
    futures_price=Decimal("112.50"),
    conversion_factor=Decimal("0.89"),
    accrued_at_delivery=Decimal("1.20"),
    face_amount=Decimal("1000000"),
)

rate = implied_repo_rate(
    spot_price=Decimal("100.40"),
    futures_price=Decimal("112.50"),
    conversion_factor=Decimal("0.89"),
    start_date=start,
    end_date=delivery,
    coupon_income=Decimal("1.50"),
    accrued_on_purchase=Decimal("0.80"),
    accrued_at_delivery=Decimal("1.20"),
    face_amount=Decimal("1000000"),
)

trade = RepoTrade(
    start_date=start,
    end_date=delivery,
    rate=Decimal("0.045"),
    collateral_price=Decimal("100.40"),
    haircut=Decimal("0.02"),
    notional=Decimal("1000000"),
)

rate_from_trade = implied_repo_rate_from_trade(
    trade,
    futures_price=Decimal("112.50"),
    conversion_factor=Decimal("0.89"),
    coupon_income=Decimal("1.50"),
    accrued_on_purchase=Decimal("0.80"),
    accrued_at_delivery=Decimal("1.20"),
)

cash_only_trade = RepoTrade(
    start_date=start,
    end_date=delivery,
    rate=Decimal("0.045"),
    collateral_price=Decimal("100.40"),
    haircut=Decimal("0.02"),
    cash_amount=Decimal("980000"),
)

cash_only_rate = implied_repo_rate_from_trade(
    cash_only_trade,
    futures_price=Decimal("112.50"),
    conversion_factor=Decimal("0.89"),
    coupon_income=Decimal("1.50"),
    accrued_on_purchase=Decimal("0.80"),
    accrued_at_delivery=Decimal("1.20"),
)

assert invoice > Decimal("0")
assert rate == rate_from_trade
assert cash_only_rate == rate

The last assertion holds because implied_repo_rate_from_trade uses face amount 100 when the trade has no notional. The implied rate is a ratio, so using 100 face or 1000000 face gives the same rate when all price inputs are the same.

Specialness

Specialness measures how much cheaper it is to borrow cash against one specific security than against general collateral. General collateral means ordinary, non-special collateral.

specialness_spread(general_collateral_rate, specific_collateral_rate) returns:

general_collateral_rate - specific_collateral_rate

The result is a raw decimal spread. Positive means the specific collateral rate is lower than the general collateral rate.

specialness_value(cash_amount, general_collateral_rate, specific_collateral_rate, year_fraction) returns:

cash_amount * specialness_spread(...) * year_fraction

The result is a currency amount.

is_special(general_collateral_rate, specific_collateral_rate) returns True when specialness_spread(...) > 0.

Example

from decimal import Decimal

from fuggers_py.funding import is_special, specialness_spread, specialness_value

gc_rate = Decimal("0.045")
specific_rate = Decimal("0.035")
cash_amount = Decimal("1000000")
year_fraction = Decimal("0.25")

spread = specialness_spread(
    general_collateral_rate=gc_rate,
    specific_collateral_rate=specific_rate,
)
value = specialness_value(
    cash_amount=cash_amount,
    general_collateral_rate=gc_rate,
    specific_collateral_rate=specific_rate,
    year_fraction=year_fraction,
)
special = is_special(
    general_collateral_rate=gc_rate,
    specific_collateral_rate=specific_rate,
)

assert spread == Decimal("0.010")
assert value == Decimal("2500.00000")
assert special is True

First-layer public facade for funding-domain objects.

class fuggers_py.funding.HaircutQuote(instrument_id, haircut=None, collateral_type=None, as_of=None, currency=None, source=None, bid=None, ask=None, mid=None)

Haircut quote record with haircut expressed as a raw decimal.

Parameters:
  • instrument_id (InstrumentId)

  • haircut (Decimal | None)

  • collateral_type (str | None)

  • as_of (Date | None)

  • currency (Currency | None)

  • source (str | None)

  • bid (Decimal | None)

  • ask (Decimal | None)

  • mid (Decimal | None)

quoted_value(side=QuoteSide.MID)

Return the side-specific haircut when present.

Return type:

Decimal | None

Parameters:

side (QuoteSide)

for_side(side)

Return a copy normalized to a different quote side.

Return type:

HaircutQuote’ | None

Parameters:

side (QuoteSide)

class fuggers_py.funding.RepoQuote(instrument_id, rate=None, haircut=None, start_date=None, end_date=None, term=None, collateral_type=None, as_of=None, currency=None, source=None, bid=None, ask=None, mid=None)

Repo quote record with rate and haircut expressed as raw decimals.

Parameters:
  • instrument_id (InstrumentId)

  • rate (Decimal | None)

  • haircut (Decimal | None)

  • start_date (Date | None)

  • end_date (Date | None)

  • term (str | None)

  • collateral_type (str | None)

  • as_of (Date | None)

  • currency (Currency | None)

  • source (str | None)

  • bid (Decimal | None)

  • ask (Decimal | None)

  • mid (Decimal | None)

quoted_value(side=QuoteSide.MID)

Return the side-specific repo rate when present.

Return type:

Decimal | None

Parameters:

side (QuoteSide)

for_side(side)

Return a copy normalized to a different quote side.

Return type:

RepoQuote’ | None

Parameters:

side (QuoteSide)

class fuggers_py.funding.RepoReferenceData(instrument_id, currency=Currency.<bound method Currency.name of <Currency.USD: 'USD'>>, collateral_currency=None, term=None, collateral_type=None, haircut=None, settlement_lag_days=None)

Repo reference record with raw decimal haircut semantics.

Parameters:
  • instrument_id (InstrumentId)

  • currency (Currency)

  • collateral_currency (Currency | None)

  • term (str | None)

  • collateral_type (str | None)

  • haircut (Decimal | None)

  • settlement_lag_days (int | None)

class fuggers_py.funding.RepoTrade(start_date, end_date, rate, collateral_price, haircut=Decimal('0'), notional=None, cash_amount=None, currency=Currency.<bound method Currency.name of <Currency.USD: 'USD'>>, day_count_convention=DayCountConvention.<bound method DayCountConvention.name of <DayCountConvention.ACT_360: 'ACT_360'>>, collateral_instrument_id=None)

Repurchase agreement trade expressed in cash and collateral terms.

Parameters:
  • start_date (Date)

  • end_date (Date)

  • rate (Decimal)

  • collateral_price (Decimal)

  • haircut (Decimal)

  • notional (Decimal | None)

  • cash_amount (Decimal | None)

  • currency (Currency)

  • day_count_convention (DayCountConvention)

  • collateral_instrument_id (InstrumentId | None)