fuggers_py.portfolio

Portfolio construction, aggregation, benchmark comparison, contribution, stress testing, liquidity summaries, and ETF-style outputs.

Use one-layer imports from fuggers_py.portfolio:

from fuggers_py.portfolio import Portfolio, Holding, calculate_portfolio_analytics

The public contract for this page is the fuggers_py.portfolio export list in specs/public_api_surface.json and src/fuggers_py/portfolio/__init__.py. The root package currently imports build_creation_basket, so direct import can work, but that name is not in __all__ or the JSON contract. Treat it as an importable helper outside the stable public surface.

Core Rules

  • A raw decimal means Decimal("0.05") for 5%.

  • A basis point is 0.01 percentage point. Fields or parameters ending in _bps use basis points.

  • Fields ending in _pct are percentage points. For example, 5 means 5%.

  • Prices are usually percent-of-par. For example, a clean price of 99.5 means 99.5% of par.

  • Currency values are in the portfolio reporting currency unless a field says otherwise.

  • DV01 and CS01 are currency changes for a one-basis-point move.

  • Portfolio weighted averages use dirty-value weights by default. Override this with AnalyticsConfig(weighting_method=...).

  • Benchmark active values are always portfolio minus benchmark.

  • In rate and spread return estimates, a positive shock means rates or spreads move up. The estimated return is usually negative when the portfolio has positive DV01 or CS01.

  • Most result objects are immutable records with named fields. Some also behave like mappings or sequences.

Examples below assume bond, second_bond, curve, settlement_date, quote_outputs, and identifier objects come from the relevant bond, curve, or quote-output pages.

Containers, Holdings, And Config

Holding, Position, HoldingAnalytics, PositionAnalytics

Holding represents one bond position. Use it when you want to attach a bond, quantity, price or market value, analytics, and classification data to a portfolio row.

The constructor converts quantity, market_value, accrued_interest, liquidity_score, and fx_rate to Decimal. clean_price may be a Price object or a decimal percent-of-par value. If analytics need a price and neither clean_price nor market_value is present, the analytics layer needs a curve.

Useful Holding members:

  • par_amount: returns quantity.

  • market_price: returns the clean price as a percent-of-par decimal, or None.

  • currency: uses classification.currency when present, otherwise the bond currency.

  • name(): returns label, then id, then the bond ISIN, then the bond class name.

  • market_value_amount: returns explicit market_value, or clean_price * quantity, or zero.

  • dirty_market_value: adds accrued interest times quantity to clean value.

  • base_currency_value: returns market_value_amount * fx_rate.

  • weight_in_portfolio(total_market_value): returns clean-value weight, or zero when the total is zero.

Position is an alias for Holding. HoldingAnalytics is the per-holding analytics record. PositionAnalytics is an alias for HoldingAnalytics.

HoldingAnalytics stores clean value, dirty value, accrued value, duration, convexity, DV01, yields, spreads, CS01, key-rate profile, liquidity score, weighted average life, and coupon. Values and PV fields are currency amounts. Yields and spreads are raw decimals unless the field name says otherwise.

HoldingBuilder

HoldingBuilder is a mutable helper for building a Holding step by step. build() raises ValueError if no instrument has been set.

Useful builder methods:

  • with_instrument(instrument)

  • with_quantity(quantity)

  • with_par_amount(par_amount), an alias for with_quantity()

  • with_clean_price(clean_price)

  • with_market_price(market_price), an alias for with_clean_price()

  • with_market_value(market_value)

  • with_accrued_interest(accrued_interest)

  • with_analytics(analytics)

  • with_label(label)

  • with_id(value)

  • with_classification(classification)

  • with_rating_info(rating_info)

  • with_sector_info(sector_info)

  • with_seniority_info(seniority_info)

  • with_liquidity_score(liquidity_score)

  • with_fx_rate(fx_rate)

  • build()

from decimal import Decimal

from fuggers_py import Currency
from fuggers_py.portfolio import (
    Classification,
    CreditRating,
    HoldingBuilder,
    Sector,
)

classification = Classification(
    sector=Sector.CORPORATE,
    rating=CreditRating.BBB,
    country="US",
    currency=Currency.USD,
    issuer="Example Issuer",
)

holding = (
    HoldingBuilder()
    .with_instrument(bond)
    .with_quantity(Decimal("1000000"))
    .with_clean_price(Decimal("99.50"))
    .with_accrued_interest(Decimal("0.75"))
    .with_classification(classification)
    .with_liquidity_score(Decimal("0.80"))
    .with_label("EXAMPLE 5.00 2030")
    .build()
)

print(holding.name())
print(holding.market_price)
print(holding.market_value_amount)
print(holding.dirty_market_value)

CashPosition

CashPosition represents cash inside a portfolio. amount and fx_rate are converted to Decimal. Cash has no clean/dirty split.

Useful members:

  • market_value(): returns the cash amount.

  • base_currency_value: returns amount * fx_rate.

from decimal import Decimal

from fuggers_py import Currency
from fuggers_py.portfolio import CashPosition

cash = CashPosition(
    amount=Decimal("250000"),
    currency=Currency.USD,
    label="operating cash",
)

print(cash.market_value())
print(cash.base_currency_value)

Portfolio, PortfolioBuilder, PortfolioMetrics

Portfolio is an immutable collection of bond holdings and cash positions in one reporting currency. It stores positions in input order.

Useful Portfolio members:

  • Portfolio.new(positions, currency): converts a mutable list into an immutable tuple.

  • total_quantity(): sums quantities on positions that have a quantity. Cash is ignored.

  • holdings(): returns all positions, including cash.

  • investable_holdings(): returns bond holdings only.

  • cash_positions(): returns cash positions only.

  • total_market_value(): sums clean market value for bonds and amount for cash.

PortfolioBuilder lets you add positions before creating the immutable portfolio. with_currency() sets the reporting currency. add_position() and add_holding() append one item. add_positions() appends many items. If no currency is set, the first added position can set it. build() raises ValueError if no currency was set or inferred.

PortfolioMetrics is the full portfolio analytics record. It holds clean PV, dirty PV, accrued value, weighted duration, convexity, DV01, weights, currency, yields, spreads, CS01, liquidity score, key-rate profile, market-value totals, cash value, counts, duration variants, weighted average maturity, and weighted average coupon.

from fuggers_py import Currency
from fuggers_py.portfolio import PortfolioBuilder

portfolio = (
    PortfolioBuilder()
    .with_currency(Currency.USD)
    .add_position(holding)
    .add_position(cash)
    .build()
)

print(portfolio.total_quantity())
print(portfolio.total_market_value())
print([item.name() for item in portfolio.investable_holdings()])

AnalyticsConfig And WeightingMethod

AnalyticsConfig controls valuation date, weighting basis, key-rate tenors, and default currency for portfolio analytics.

Fields:

  • settlement_date: date used for accrued interest and valuation.

  • weighting_method: one of DIRTY_VALUE, CLEAN_VALUE, MARKET_VALUE, or EQUAL.

  • key_rate_tenors: tenors used to build key-rate profiles.

  • default_currency: currency used when a default is needed.

The default weighting method is WeightingMethod.DIRTY_VALUE.

from fuggers_py.portfolio import (
    AnalyticsConfig,
    PortfolioAnalytics,
    WeightingMethod,
)

config = AnalyticsConfig(
    settlement_date=settlement_date,
    weighting_method=WeightingMethod.CLEAN_VALUE,
)

metrics = PortfolioAnalytics(portfolio).metrics(
    curve,
    settlement_date,
    config=config,
)

print(metrics.weights)
print(metrics.duration)

Classification Objects

These records attach grouping data to holdings.

  • Classification: optional sector, rating, seniority, country, currency, issuer, region, and custom fields.

  • CreditRating: enum values from AAA through D, plus NR.

  • RatingInfo: rating plus optional agency and outlook.

  • Sector: high-level sector enum, such as GOVERNMENT, CORPORATE, ETF, CASH, and OTHER.

  • SectorInfo: sector plus optional issuer, country, region, and subsector.

  • Seniority: seniority enum, such as SENIOR_SECURED or SUBORDINATED.

  • SeniorityInfo: seniority plus secured boolean.

  • RatingBucket: a label and tuple of CreditRating values.

  • MaturityBucket: a half-open maturity range in years.

Useful methods and properties:

  • CreditRating.score(): lower score means stronger credit. NR returns 99.

  • MaturityBucket.contains(years_to_maturity): checks whether a maturity is in the bucket.

from fuggers_py.portfolio import CreditRating, MaturityBucket

print(CreditRating.BBB.score())

bucket = MaturityBucket("2-5Y", 2.0, 5.0)
print(bucket.contains(3.25))
print(bucket.contains(5.0))

Shared Result Records

  • BucketResult: bucket-level clean PV, dirty PV, DV01, weight, market value, average YTM, average duration, average spread, and holding count.

  • StressResult: base dirty PV, stressed dirty PV, signed actual_change, dv01_approximation, optional scenario name, and optional breakdown.

  • StressResult.shocked_pv: alias for stressed_dirty_pv.

  • StressResult.pv_change: alias for actual_change.

actual_change is stressed value minus base value. Negative values are losses.

Portfolio Analytics And NAV

PortfolioAnalytics

PortfolioAnalytics wraps a portfolio and runs valuation on each position. position_metrics(curve, settlement_date, ...) returns one PositionAnalytics record per portfolio position. metrics(curve, settlement_date, ...) aggregates those records into PortfolioMetrics.

When a holding already has analytics, the analytics layer uses that record. Otherwise it prices from clean_price, then from market_value / quantity, or from curve. If no price or curve is available, analytics can raise ValueError.

calculate_portfolio_analytics(portfolio, curve=..., settlement_date=...) is the one-call version of PortfolioAnalytics(portfolio).metrics(...).

from fuggers_py.portfolio import (
    PortfolioAnalytics,
    calculate_portfolio_analytics,
)

analytics = PortfolioAnalytics(portfolio)

position_rows = analytics.position_metrics(curve, settlement_date)
for row in position_rows:
    print(row.name, row.clean_value, row.dirty_value, row.dv01)

metrics = calculate_portfolio_analytics(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)

print(metrics.dirty_pv)
print(metrics.duration)
print(metrics.weights)

Quote-Output Portfolio Analytics

PortfolioPosition is a light record with instrument_id and quantity.

PortfolioAnalyzer aggregates already-computed BondQuoteOutput records into PortfolioAnalyticsOutput. It skips positions with no quote or no clean price. It value-weights duration, convexity, and spreads by dirty value. If reference data with sector and rating is supplied, it also returns sector and rating breakdowns.

from decimal import Decimal

from fuggers_py.portfolio import PortfolioAnalyzer, PortfolioPosition

positions = [
    PortfolioPosition(instrument_id=first_instrument_id, quantity=Decimal("1000")),
    PortfolioPosition(instrument_id=second_instrument_id, quantity=Decimal("500")),
]

output = PortfolioAnalyzer().analyze(
    portfolio_id="portfolio:demo",
    positions=positions,
    quote_outputs=quote_outputs,
)

print(output.total_market_value)
print(output.fully_priced)

Risk, Yield, Spread, And Credit

Risk And Duration

RiskMetrics stores duration, modified duration, effective duration, Macaulay duration, best duration, convexity, effective convexity, DV01, and CS01.

Functions:

  • calculate_risk_metrics: returns RiskMetrics.

  • weighted_modified_duration: returns PortfolioMetrics.modified_duration.

  • weighted_effective_duration: returns PortfolioMetrics.effective_duration.

  • weighted_macaulay_duration: returns PortfolioMetrics.macaulay_duration.

  • weighted_effective_convexity: returns effective convexity.

  • weighted_best_duration: uses effective duration when present, otherwise modified duration, weighted by dirty value.

Yield

YieldMetrics stores YTM, YTW, YTC, current yield, and best yield as raw decimals.

Functions:

  • calculate_yield_metrics: returns YieldMetrics.

  • weighted_best_yield: returns the portfolio best yield.

Spread

SpreadMetrics stores Z-spread, OAS, G-spread, I-spread, asset-swap spread, best spread, spread duration, and CS01. Spreads are raw decimals.

Functions:

  • calculate_spread_metrics: returns SpreadMetrics.

  • weighted_z_spread, weighted_oas, weighted_g_spread, weighted_i_spread, and weighted_asw: return individual spread fields.

  • weighted_best_spread: returns the best available spread.

  • weighted_spread_duration: returns spread duration.

Credit Quality

CreditQualityMetrics stores rating distribution, sector distribution, average rating score, average rating, investment-grade weight, high-yield weight, default weight, unrated weight, BBB weight, BB weight, crossover weight, quality tiers, and migration risk.

CreditMetrics is an alias for CreditQualityMetrics.

QualityTiers stores investment-grade, high-yield, distressed, defaulted, and unrated weights.

FallenAngelRisk stores BBB weight, market value at risk, and holding count. Its weight property returns bbb_weight.

RisingStarRisk stores BB weight, market value potential, and holding count. Its weight property returns bb_weight.

MigrationRisk combines fallen-angel and rising-star risk. Its crossover_weight property returns BBB weight plus BB weight.

Functions:

  • calculate_credit_quality: returns CreditQualityMetrics.

  • calculate_credit_metrics: alias for calculate_credit_quality.

  • calculate_migration_risk: returns MigrationRisk.

from fuggers_py.portfolio import (
    calculate_credit_quality,
    calculate_risk_metrics,
    calculate_spread_metrics,
    calculate_yield_metrics,
    weighted_best_duration,
    weighted_oas,
)

risk = calculate_risk_metrics(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
print(risk.duration, risk.dv01)

yields = calculate_yield_metrics(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
print(yields.ytm, yields.best_yield)

spreads = calculate_spread_metrics(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
print(spreads.z_spread, spreads.cs01)

credit = calculate_credit_quality(portfolio)
print(credit.distribution)
print(credit.migration_risk.crossover_weight)

print(weighted_best_duration(portfolio, curve=curve, settlement_date=settlement_date))
print(weighted_oas(portfolio, curve=curve, settlement_date=settlement_date))

Liquidity

Liquidity scores are expected to be 0-to-1 decimals. Higher is more liquid. If a holding has no liquidity score, portfolio analytics treat it as 1.

The bucket labels are fixed:

  • high: score >= 0.75

  • medium: 0.50 <= score < 0.75

  • limited: 0.25 <= score < 0.50

  • illiquid: score < 0.25

estimate_days_to_liquidate uses a simple formula: 1 + (1 - liquidity_score) * 9, then multiplies by liquidation_fraction. liquidation_fraction must be between 0 and 1.

Records:

  • LiquidityBucket: label, min score, max score, weight, dirty PV, and holding count.

  • LiquidityDistribution: mapping from bucket label to LiquidityBucket. It supports keys(), values(), items(), total_weight, and total_dirty_pv.

  • DaysToLiquidate: days, liquidity score, and liquidation fraction. It also behaves like a mapping over its fields.

  • LiquidityMetrics: liquidity score, bid-ask spread, days to liquidate, and distribution. It also behaves like a mapping over its fields.

Functions:

  • weighted_liquidity_score: weighted portfolio liquidity score.

  • weighted_bid_ask_spread: dirty-value-weighted bid-ask spread. It reads bid_ask_spread, bid_ask, or bidask_spread from holding custom fields when present.

  • liquidity_distribution: fixed bucket distribution.

  • estimate_days_to_liquidate: simple days estimate.

  • calculate_liquidity_metrics: full liquidity record.

from decimal import Decimal

from fuggers_py.portfolio import (
    calculate_liquidity_metrics,
    estimate_days_to_liquidate,
    liquidity_distribution,
)

liquidity = calculate_liquidity_metrics(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)

print(liquidity["liquidity_score"])
print(liquidity.days_to_liquidate.days)

distribution = liquidity_distribution(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)

for label, bucket in distribution.items():
    print(label, bucket.weight, bucket.holding_count)

half_sale = estimate_days_to_liquidate(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    liquidation_fraction=Decimal("0.50"),
)
print(half_sale.days)

Bucketing And Distributions

Bucketing helpers group holdings into named buckets. Cash is included only in the maturity helper, where it is grouped under Cash.

Records:

  • MaturityDistribution: mapping from maturity bucket label to positions. It also stores the bucket definition.

  • RatingDistribution: mapping from rating label to positions.

  • SectorDistribution: mapping from sector label to positions.

  • ClassifierDistribution: mapping from a named classifier to positions.

  • CustomDistribution: mapping from a custom field to positions.

  • BucketMetrics: alias for BucketResult.

  • Bucketing: wrapper with bucket_dv01(curve, settlement_date, buckets=...).

Distribution objects support keys(), values(), items(), get(), as_dict(), bucket_count, and holding_count.

Functions:

  • bucket_by_maturity: buckets by years to maturity using DEFAULT_BUCKETS. Default buckets are 0-2Y, 2-5Y, 5-10Y, and 10Y+.

  • bucket_by_rating: uses rating_info, then classification.rating, then NR.

  • bucket_by_sector: uses sector_info, then classification.sector, then OTHER.

  • bucket_by_country, bucket_by_currency, bucket_by_issuer, and bucket_by_region: return plain dictionaries keyed by classification field.

  • bucket_by_custom_field: uses holding.custom_fields, then classification.custom_fields, then UNKNOWN.

  • bucket_by_classifier: supports rating, sector, seniority, country, region, issuer, currency, custom fields, and other classification fields. Missing values use UNKNOWN.

from fuggers_py.portfolio import (
    Bucketing,
    bucket_by_classifier,
    bucket_by_custom_field,
    bucket_by_maturity,
    bucket_by_rating,
    bucket_by_sector,
)

maturity = bucket_by_maturity(
    portfolio,
    settlement_date=settlement_date,
)
print(maturity.bucket_count)
print(maturity.holding_count)

rating = bucket_by_rating(portfolio)
for rating_label, positions in rating.items():
    print(rating_label, len(positions))

sector = bucket_by_sector(portfolio)
print(sector.as_dict())

custom = bucket_by_custom_field(portfolio, "strategy")
print(custom.keys())

issuer = bucket_by_classifier(portfolio, "issuer")
print(issuer.items())

bucket_metrics = Bucketing(portfolio).bucket_dv01(
    curve,
    settlement_date,
)
for bucket in bucket_metrics:
    print(bucket.label, bucket.dv01, bucket.average_duration)

Benchmark Comparison

Benchmark helpers compare two portfolios on active weights, duration, yield, spread, and risk. Active means portfolio minus benchmark.

Records:

  • ActiveWeight: one named active weight. value returns active_weight. as_dict() returns a simple dictionary. __getitem__ supports name, portfolio_weight, benchmark_weight, active_weight, and value.

  • ActiveWeights: mapping from name to active weight. It supports keys(), values(), items(), get(), by_name(), to_dict(), portfolio_weights, benchmark_weights, and net_active_weight.

  • DurationComparison: portfolio, benchmark, and active duration.

  • RiskComparison: portfolio, benchmark, and active dirty PV and DV01.

  • YieldComparison: portfolio, benchmark, and active current yield, YTM, and YTW.

  • SpreadComparison: portfolio, benchmark, and active Z-spread and OAS.

  • SectorComparison: portfolio, benchmark, and active sector weights.

  • RatingComparison: portfolio, benchmark, and active rating weights.

  • BenchmarkComparison: full comparison result. It exposes convenience properties for active dirty PV, duration, DV01, current yield, Z-spread, YTM, YTW, OAS, sector active weights, and rating active weights.

  • BenchmarkMetrics: alias for BenchmarkComparison.

  • PortfolioBenchmark: reusable pair of portfolio and benchmark.

  • TrackingErrorEstimate: heuristic tracking-error result. It stores the total estimate and duration, spread, and dispersion components. It can be compared with decimals and has as_decimal().

Functions:

  • active_weights: active holding weights.

  • compare_portfolios: full comparison.

  • benchmark_comparison: alias for compare_portfolios.

  • estimate_tracking_error: heuristic estimate from active duration, active spread, and active-weight dispersion. It is not a fitted statistical model.

Useful PortfolioBenchmark methods:

  • compare(curve, settlement_date)

  • active_weights(curve, settlement_date)

  • active_weights_by_holding(curve, settlement_date)

  • active_weights_by_sector(curve, settlement_date)

  • active_weights_by_rating(curve, settlement_date)

  • aggregated_attribution(curve, settlement_date, assumptions=None)

  • duration_difference_by_sector(curve, settlement_date)

  • spread_difference_by_sector(curve, settlement_date)

  • overweight_underweight_counts(curve, settlement_date)

  • largest_active_positions(curve, settlement_date, limit=5)

  • tracking_error_estimate(curve, settlement_date)

from fuggers_py.portfolio import (
    PortfolioBenchmark,
    active_weights,
    compare_portfolios,
    estimate_tracking_error,
)

comparison = compare_portfolios(
    portfolio,
    benchmark_portfolio,
    curve,
    settlement_date,
)
print(comparison.active_duration)
print(comparison.sector_active_weights.to_dict())

weights = active_weights(
    portfolio,
    benchmark_portfolio,
    curve,
    settlement_date,
)
for name, active_weight in weights.items():
    print(name, active_weight)

pair = PortfolioBenchmark(portfolio, benchmark_portfolio)
print(pair.largest_active_positions(curve, settlement_date, limit=3))
print(pair.overweight_underweight_counts(curve, settlement_date))

tracking = estimate_tracking_error(pair, curve, settlement_date)
print(tracking.as_decimal())
print(tracking.duration_component)

Contribution And Attribution

Contribution helpers split portfolio values back to holdings or sectors. Attribution helpers estimate return pieces from income, rate moves, and spread moves.

The active convention is portfolio minus benchmark. Rate and spread return estimates use this first-order formula:

  • rate return: -(portfolio DV01 * rate_change_bps) / dirty PV

  • spread return: -(portfolio CS01 * spread_change_bps) / dirty PV

Records:

  • Contribution: wrapper for portfolio contribution helpers.

  • HoldingContribution: one holding contribution. Properties include contribution, duration_contribution, dv01_contribution, spread_contribution, and cs01_contribution. It supports as_dict() and key lookup.

  • DurationContributions: sequence of HoldingContribution records with total.

  • Dv01Contributions: sequence of HoldingContribution records with total.

  • Cs01Contributions: sequence of HoldingContribution records with total.

  • SpreadContributions: alias for Cs01Contributions.

  • HoldingAttribution: holding-level PV percent, DV01 percent, and duration contribution. It supports as_dict() and key lookup.

  • PortfolioAttribution: sequence of HoldingAttribution records. It stores total PV percent, total DV01 percent, total duration contribution, and supports by_name(name).

  • AttributionInput: income horizon in years, rate shock in basis points, and spread shock in basis points. It converts values to Decimal and raises ValueError when income_horizon_years is negative. Its aggregate() method calls aggregated attribution.

  • BucketContribution: portfolio value, benchmark value, active value, and weights for one bucket. value returns active_value.

  • SectorAttribution: mapping from sector name to BucketContribution. It supports keys(), values(), items(), by_name(), and total_active.

  • AggregatedAttribution: income, rate, spread, and total return estimates. When a benchmark is provided, benchmark and active fields are also populated. from_portfolios() is a constructor helper.

Functions:

  • duration_contributions: holding duration contributions weighted by dirty PV.

  • dv01_contributions: holding DV01 contributions.

  • spread_contributions: holding CS01 contributions.

  • cs01_contributions: alias for spread_contributions.

  • top_contributors: sorts contribution records or dictionaries by a selected value key.

  • attribution_summary: holding-level PV and DV01 attribution.

  • calculate_attribution: alias for attribution_summary.

  • estimate_income_returns: current yield times horizon.

  • estimate_rate_returns: first-order rate return estimate.

  • estimate_spread_returns: first-order spread return estimate.

  • duration_difference_by_sector: sector duration difference versus benchmark.

  • spread_difference_by_sector: sector spread difference versus benchmark.

  • aggregated_attribution: combined income, rate, and spread attribution.

Useful Contribution methods:

  • by_position(curve, settlement_date)

  • aggregate(curve, settlement_date, assumptions=None)

from decimal import Decimal

from fuggers_py.portfolio import (
    AttributionInput,
    Contribution,
    aggregated_attribution,
    attribution_summary,
    duration_contributions,
    dv01_contributions,
    estimate_rate_returns,
    top_contributors,
)

duration = duration_contributions(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
print(duration.total)
print(duration.by_name("EXAMPLE 5.00 2030"))

dv01 = dv01_contributions(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
leaders = top_contributors(
    dv01,
    value_key="dv01_contribution",
    limit=3,
    absolute=True,
)
for item in leaders:
    print(item.as_dict())

summary = attribution_summary(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
)
print(summary.total_pv_pct)
print(summary.by_name("EXAMPLE 5.00 2030"))

assumptions = AttributionInput(
    income_horizon_years=Decimal("1"),
    rate_change_bps=Decimal("25"),
    spread_change_bps=Decimal("50"),
)
aggregate = aggregated_attribution(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    assumptions=assumptions,
    benchmark=benchmark_portfolio,
)
print(aggregate.active_total_return)

wrapper = Contribution(portfolio)
print(wrapper.aggregate(curve, settlement_date, assumptions=assumptions).total_return)

rate_return = estimate_rate_returns(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    rate_change_bps=Decimal("10"),
)
print(rate_return)

ETF Workflows

ETF helpers build basket summaries, NAV metrics, premium/discount metrics, SEC-yield records, and simple compliance checks.

Basket Records

  • BasketAnalysis: high-level basket summary with number of positions, sector counts, and total quantity. security_count returns num_positions.

  • BasketComponent: one security in a creation basket.

  • BasketFlowSummary: security value, dirty value, accrued interest, cash, liabilities, total basket value, shares outstanding, and creation-unit shares. basket_per_share returns total basket value divided by creation-unit shares, or zero when creation-unit shares are zero.

  • CreationBasket: ordered sequence of BasketComponent records plus a flow summary. It supports iteration, len(), indexing, by_name(name), component_count, and basket_per_share.

Functions:

  • analyze_etf_basket: counts portfolio positions, sector counts, and total quantity.

  • build_creation_basket: importable from fuggers_py.portfolio today, but not listed in __all__ or the JSON public contract. It scales portfolio holdings to a requested creation-unit share count and requires positive shares_outstanding and creation_unit_shares.

from decimal import Decimal

from fuggers_py.portfolio import analyze_etf_basket, build_creation_basket

analysis = analyze_etf_basket(portfolio)
print(analysis.security_count)
print(analysis.sector_counts)

basket = build_creation_basket(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    shares_outstanding=Decimal("1000000"),
    creation_unit_shares=Decimal("50000"),
)

print(basket.component_count)
print(basket.basket_per_share)

component = basket.by_name("EXAMPLE 5.00 2030")
if component is not None:
    print(component.quantity, component.weight)

SEC Yield, Distribution Yield, Expenses, And Compliance

Records:

  • SecYieldInput: inputs for standardized SEC-yield calculation.

  • SecYield: standardized SEC-yield result. fee_waiver_impact() returns subsidized yield minus unsubsidized yield when both are available.

  • DistributionYield: distribution yield as raw decimal, percentage points, and basis points. yield_pct returns distribution_yield_pct. It also delegates numeric operations to the raw decimal value.

  • ExpenseMetrics: gross yield, net yield, expense ratios, fee waiver ratio, annual income estimate, annual expense amount, and net assets. Properties expense_drag, yield_before_expenses, and yield_after_expenses return the main yield pieces.

  • ComplianceSeverity: INFO, WARNING, or CRITICAL.

  • ComplianceCheck: one named compliance check.

  • EtfComplianceReport: weight and issuer-limit checks. passed is true only when all checks pass. by_name(name) returns a check by name.

Functions:

  • calculate_sec_yield(SecYieldInput(...)): returns a SecYield record.

  • calculate_sec_yield(net_investment_income, net_assets): legacy call form. It returns a raw decimal and emits a deprecation warning.

  • approximate_sec_yield: historical approximation as a raw decimal.

  • calculate_distribution_yield: annual distribution divided by market price.

  • estimate_yield_from_holdings: estimates gross and net yield from portfolio YTM and expense ratios.

  • etf_compliance_checks: checks whether weights sum to one and whether the largest issuer weight is at or below the fixed 25% limit.

Validation:

  • SEC-yield average shares and maximum offering price must be positive.

  • Approximate SEC-yield net assets must be positive.

  • Distribution-yield market price must be positive.

  • Gross expense ratio and fee waiver ratio cannot be negative.

  • Issuer weight cannot be negative.

  • Weight sum passes when it is within 0.0001 of one.

from decimal import Decimal

from fuggers_py.portfolio import (
    SecYieldInput,
    calculate_distribution_yield,
    calculate_sec_yield,
    estimate_yield_from_holdings,
    etf_compliance_checks,
)

sec_input = SecYieldInput(
    net_investment_income=Decimal("250000"),
    average_shares_outstanding=Decimal("1000000"),
    max_offering_price=Decimal("100"),
    fee_waivers=Decimal("10000"),
)
sec_yield = calculate_sec_yield(sec_input)
print(sec_yield.sec_30_day_yield)
print(sec_yield.fee_waiver_impact())

distribution = calculate_distribution_yield(
    annual_distribution=Decimal("4.80"),
    market_price=Decimal("100"),
)
print(distribution.distribution_yield)
print(distribution.yield_pct)

expenses = estimate_yield_from_holdings(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    gross_expense_ratio=Decimal("0.0040"),
    fee_waiver_ratio=Decimal("0.0010"),
)
print(expenses.expense_drag)
print(expenses.yield_after_expenses)

report = etf_compliance_checks(
    holdings_weight_sum=Decimal("1.0000"),
    max_issuer_weight=Decimal("0.20"),
)
print(report.passed)
print(report.by_name("issuer_limit_ok").severity)

Quote-Output ETF Pricing

EtfPricer aggregates EtfHolding rows and BondQuoteOutput records into an EtfAnalyticsOutput. It skips holdings without quotes, prices, or quantities. It value-weights duration, convexity, and spreads by dirty value. price() returns NAV, iNAV, per-share risk, counts, and weighted risk fields.

from decimal import Decimal

from fuggers_py.portfolio import EtfPricer

etf_output = EtfPricer().price(
    etf_id="etf:demo",
    holdings=etf_holdings,
    quote_outputs=quote_outputs,
    shares_outstanding=Decimal("1000000"),
)

print(etf_output.nav)
print(etf_output.fully_priced)

Stress

Stress helpers apply rate, spread, or tenor-specific shocks to portfolio analytics and return dirty-PV changes.

Records and aliases:

  • StressScenario: base named scenario.

  • RateShockScenario: parallel rate shock in basis points.

  • RateScenario: alias for RateShockScenario.

  • SpreadShockScenario: parallel spread shock in basis points.

  • SpreadScenario: alias for SpreadShockScenario.

  • TenorShift: one tenor and one shock in basis points.

  • KeyRateShiftScenario: name plus tenor-to-shock mapping. tenor_shifts returns typed TenorShift records. from_tenor_shifts() builds a scenario from typed shifts.

  • StressSummary: mapping from scenario name to StressResult. It supports from_results(), scenario_count, aggregate_change, worst_loss, best_gain, best_case(), and worst_case().

  • Stress: wrapper with parallel_shift(curve, settlement_date, bump_bps=...).

Functions:

  • rate_shock_impact: typed result for a parallel rate shock.

  • parallel_shift_impact: alias for rate_shock_impact.

  • spread_shock_impact: decimal dirty-PV change for a spread shock.

  • spread_shock_result: typed result for a spread shock.

  • key_rate_shift_impact: decimal dirty-PV change for tenor shocks.

  • key_rate_shift_result: typed result for tenor shocks.

  • run_stress_scenario: runs one scenario and raises TypeError for unsupported scenario types.

  • run_stress_scenarios: runs supported scenarios and returns StressSummary.

  • stress_scenarios: alias for run_stress_scenarios.

  • standard_scenarios: returns the package standard scenarios.

  • summarize_results: normalizes mappings or iterables into StressSummary.

  • best_case: best result from a result collection.

  • worst_case: worst result from a result collection.

For rate and spread shocks, positive bump_bps usually produces a negative PV change when the portfolio has positive DV01 or CS01.

from decimal import Decimal

from fuggers_py.portfolio import (
    KeyRateShiftScenario,
    RateShockScenario,
    SpreadShockScenario,
    Stress,
    TenorShift,
    run_stress_scenario,
    run_stress_scenarios,
    standard_scenarios,
)

rate_up = RateShockScenario(
    name="+25 bps rates",
    bump_bps=Decimal("25"),
)
result = run_stress_scenario(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    scenario=rate_up,
)
print(result.actual_change)
print(result.pv_change)

key_rate = KeyRateShiftScenario.from_tenor_shifts(
    name="curve twist",
    shifts=[
        TenorShift("2Y", Decimal("-10")),
        TenorShift("10Y", Decimal("15")),
    ],
)

summary = run_stress_scenarios(
    portfolio,
    curve=curve,
    settlement_date=settlement_date,
    scenarios=[
        rate_up,
        SpreadShockScenario(name="+50 bps spreads", bump_bps=Decimal("50")),
        key_rate,
    ],
)

print(summary.scenario_count)
print(summary.worst_loss)
print(summary.best_case())

wrapper_result = Stress(portfolio).parallel_shift(
    curve,
    settlement_date,
    bump_bps=Decimal("10"),
)
print(wrapper_result.shocked_pv)

for scenario in standard_scenarios():
    print(scenario.name)

Public Submodules

The package exports these module objects for users who prefer grouped imports:

  • analytics

  • benchmark

  • bucketing

  • contribution

  • etf

  • liquidity

  • risk

  • stress

  • types

User code should still prefer one-layer imports from fuggers_py.portfolio unless it has a clear reason to import a submodule.

Boundaries

  • Bond instruments and bond analytics come from fuggers_py.bonds.

  • Curves come from fuggers_py.curves.

  • Credit instruments and CDS analytics come from fuggers_py.credit.

  • Rates instruments come from fuggers_py.rates.

  • Inflation data and inflation swaps come from fuggers_py.inflation.

  • Funding trades and financing analytics come from fuggers_py.funding.

portfolio can depend on these packages because portfolio work combines many domains. Those packages should not import from portfolio.

Fixed-income portfolio analytics and typed public result surfaces.

The package exposes the main portfolio container, typed holdings and classification helpers, and the analytics surfaces for risk, spreads, yield, liquidity, ETF, benchmark, contribution, bucketing, stress, and quote-output aggregation workflows.

The main public groups are:

  • analytics for totals, weighted metrics, NAV, and key-rate profiles

  • benchmark for active-weight and tracking-error style comparisons

  • contribution for attribution and grouped risk splits

  • etf for basket, NAV, and compliance helpers

  • stress for parallel, spread, and key-rate scenario analysis

Values are expressed using the library’s fixed-income conventions: prices are typically percent-of-par, spreads and rates are raw decimals unless a *_bps or *_pct field states otherwise, and portfolio analytics are settled on the valuation date passed to each helper.

fuggers_py.portfolio.aggregate_key_rate_profile(portfolio, *, curve, settlement_date)

Return the portfolio key-rate DV01 profile.

Parameters:
  • portfolio (Portfolio) – Portfolio to aggregate.

  • curve – Yield curve input used to price the holdings.

  • settlement_date – Valuation date used for the portfolio metrics.

Returns:

Tenor-keyed DV01 entries in currency units per 1 bp.

Return type:

KeyRateProfile

class fuggers_py.portfolio.AggregatedAttribution(assumptions, income_return, rate_return, spread_return, total_return, benchmark_income_return=None, benchmark_rate_return=None, benchmark_spread_return=None, benchmark_total_return=None, active_income_return=None, active_rate_return=None, active_spread_return=None, active_total_return=None, duration_by_sector=None, spread_by_sector=None)

Aggregated income, rate, and spread attribution outputs.

Parameters:
  • assumptions (AttributionInput)

  • income_return (Decimal)

  • rate_return (Decimal)

  • spread_return (Decimal)

  • total_return (Decimal)

  • benchmark_income_return (Decimal | None)

  • benchmark_rate_return (Decimal | None)

  • benchmark_spread_return (Decimal | None)

  • benchmark_total_return (Decimal | None)

  • active_income_return (Decimal | None)

  • active_rate_return (Decimal | None)

  • active_spread_return (Decimal | None)

  • active_total_return (Decimal | None)

  • duration_by_sector (SectorAttribution | None)

  • spread_by_sector (SectorAttribution | None)

classmethod from_portfolios(portfolio, *, curve, settlement_date, assumptions=None, benchmark=None)

Build aggregated attribution from one or two portfolios.

Return type:

AggregatedAttribution

Parameters:
class fuggers_py.portfolio.ActiveWeight(name, portfolio_weight, benchmark_weight, active_weight)

Active weight for a single holding or bucket.

Parameters:
  • name (str)

  • portfolio_weight (Decimal)

  • benchmark_weight (Decimal)

  • active_weight (Decimal)

property value: Decimal

Return the active weight.

as_dict()

Return a mapping-style representation.

Return type:

dict[str, Decimal | str]

class fuggers_py.portfolio.ActiveWeights(entries, dimension='holding')

Collection of active weights keyed by name.

Parameters:
keys()

Return the active-weight names.

Return type:

tuple[str, ...]

values()

Return the active-weight values.

Return type:

tuple[Decimal, ...]

items()

Return (name, active_weight) pairs.

Return type:

tuple[tuple[str, Decimal], ...]

get(key, default=None)

Return the active weight for key or default.

Return type:

Decimal | None

Parameters:
  • key (str)

  • default (Decimal | None)

by_name(name)

Return the matching entry if present.

Return type:

ActiveWeight | None

Parameters:

name (str)

property portfolio_weights: dict[str, Decimal]

Return portfolio weights by name.

property benchmark_weights: dict[str, Decimal]

Return benchmark weights by name.

property net_active_weight: Decimal

Return the signed sum of active weights.

to_dict()

Return a plain dictionary of active weights.

Return type:

dict[str, Decimal]

fuggers_py.portfolio.analyze_etf_basket(portfolio)

Return a high-level basket analysis for the portfolio.

Return type:

BasketAnalysis

Parameters:

portfolio (Portfolio)

class fuggers_py.portfolio.AnalyticsConfig(settlement_date=None, weighting_method=WeightingMethod.DIRTY_VALUE, key_rate_tenors=(Tenor(length=3, unit=<TenorUnit.MONTHS: 'M'>), Tenor(length=6, unit=<TenorUnit.MONTHS: 'M'>), Tenor(length=1, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=2, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=3, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=5, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=7, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=10, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=20, unit=<TenorUnit.YEARS: 'Y'>), Tenor(length=30, unit=<TenorUnit.YEARS: 'Y'>)), default_currency=Currency.<bound method Currency.name of <Currency.USD: 'USD'>>)

Configuration for portfolio analytics aggregation.

The config controls the settlement date used for accrued interest, the weighting basis used for portfolio averages, and the key-rate tenors used when building tenor profiles.

Parameters:
  • settlement_date (Date | None)

  • weighting_method (WeightingMethod)

  • key_rate_tenors (tuple[Tenor, ...])

  • default_currency (Currency)

fuggers_py.portfolio.approximate_sec_yield(net_investment_income, net_assets)

Return the historical SEC-yield approximation as a raw decimal.

Return type:

Decimal

Parameters:
  • net_investment_income (Decimal)

  • net_assets (Decimal)

fuggers_py.portfolio.arbitrage_opportunity(portfolio, *, curve, settlement_date, shares_outstanding, market_price, liabilities=Decimal('0'), transaction_cost_bps=Decimal('0'))

Evaluate ETF creation or redemption arbitrage against NAV.

The result compares market price to NAV after estimated transaction costs and flags whether create or redeem is the better direction.

Return type:

PremiumDiscountPoint

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

  • market_price (Decimal)

  • liabilities (Decimal)

  • transaction_cost_bps (Decimal)

class fuggers_py.portfolio.AttributionInput(income_horizon_years=Decimal('1'), rate_change_bps=Decimal('0'), spread_change_bps=Decimal('0'))

Assumptions used to scale attribution return estimates.

Parameters:
  • income_horizon_years (Decimal)

  • rate_change_bps (Decimal)

  • spread_change_bps (Decimal)

aggregate(portfolio, *, curve, settlement_date, benchmark=None)

Aggregate attribution using these assumptions.

Return type:

AggregatedAttribution

Parameters:
fuggers_py.portfolio.attribution_summary(portfolio, *, curve, settlement_date)

Return holding-level PV and DV01 attribution.

The PV and DV01 percentages are normalized against the portfolio totals computed by the analytics layer. The duration contribution is adjusted so the holding totals reconcile to the portfolio-level duration.

Return type:

PortfolioAttribution

Parameters:

portfolio (Portfolio)

class fuggers_py.portfolio.BasketAnalysis(num_positions, sector_counts, total_quantity)

High-level ETF basket summary.

Parameters:
  • num_positions (int)

  • sector_counts (dict[str, int])

  • total_quantity (Decimal)

property security_count: int

Return the number of securities in the basket.

class fuggers_py.portfolio.BasketComponent(name, quantity, clean_price, dirty_price, market_value, dirty_value, accrued_interest, weight, sector=None)

One component in a creation basket.

Parameters:
  • name (str)

  • quantity (Decimal)

  • clean_price (Decimal | None)

  • dirty_price (Decimal | None)

  • market_value (Decimal)

  • dirty_value (Decimal)

  • accrued_interest (Decimal)

  • weight (Decimal)

  • sector (str | None)

class fuggers_py.portfolio.BasketFlowSummary(component_count, total_quantity, securities_market_value, securities_dirty_value, accrued_interest, cash_component, liabilities_component, total_basket_value, shares_outstanding, creation_unit_shares)

Creation-basket cash-flow summary in currency units.

Parameters:
  • component_count (int)

  • total_quantity (Decimal)

  • securities_market_value (Decimal)

  • securities_dirty_value (Decimal)

  • accrued_interest (Decimal)

  • cash_component (Decimal)

  • liabilities_component (Decimal)

  • total_basket_value (Decimal)

  • shares_outstanding (Decimal)

  • creation_unit_shares (Decimal)

property basket_per_share: Decimal

Return the basket value per ETF share.

class fuggers_py.portfolio.BenchmarkComparison(risk, duration, yields, spread, active_weights, sector, rating)

Complete portfolio-versus-benchmark comparison output.

Parameters:
property active_dirty_pv: Decimal

Return the active dirty PV.

property active_duration: Decimal

Return the active duration.

property active_dv01: Decimal

Return the active DV01.

property active_current_yield: Decimal

Return the active current yield.

property active_z_spread: Decimal

Return the active Z-spread.

property active_ytm: Decimal

Return the active YTM.

property active_ytw: Decimal

Return the active YTW.

property active_oas: Decimal

Return the active OAS.

property sector_active_weights: ActiveWeights

Return the sector active weights.

property rating_active_weights: ActiveWeights

Return the rating active weights.

fuggers_py.portfolio.BenchmarkMetrics

alias of BenchmarkComparison

fuggers_py.portfolio.best_case(results)

Return the best-case stress result, if any.

Return type:

StressResult | None

Parameters:

results (StressSummary | Mapping[str, StressResult] | Iterable[StressResult])

class fuggers_py.portfolio.BucketContribution(name, portfolio_value, benchmark_value, active_value, portfolio_weight=Decimal('0'), benchmark_weight=Decimal('0'), active_weight=Decimal('0'))

Portfolio-versus-benchmark contribution for a bucket.

Parameters:
  • name (str)

  • portfolio_value (Decimal)

  • benchmark_value (Decimal)

  • active_value (Decimal)

  • portfolio_weight (Decimal)

  • benchmark_weight (Decimal)

  • active_weight (Decimal)

property value: Decimal

Return the active value.

fuggers_py.portfolio.BucketMetrics

alias of BucketResult

class fuggers_py.portfolio.BucketResult(label, clean_pv, dirty_pv, dv01, weight=Decimal('0'), market_value=Decimal('0'), average_ytm=None, average_duration=None, average_spread=None, holding_count=0)

Aggregate metrics for a single portfolio bucket.

Parameters:
  • label (str)

  • clean_pv (Decimal)

  • dirty_pv (Decimal)

  • dv01 (Decimal)

  • weight (Decimal)

  • market_value (Decimal)

  • average_ytm (Decimal | None)

  • average_duration (Decimal | None)

  • average_spread (Decimal | None)

  • holding_count (int)

label

Bucket name.

clean_pv, dirty_pv

Aggregated clean and dirty present values in currency units.

dv01

Bucket DV01 in currency units per 1 bp.

weight

Bucket weight relative to the portfolio total, as a raw decimal.

market_value

Clean market value proxy used by the bucketing helpers.

average_ytm, average_duration, average_spread

Dirty-value-weighted averages, or None when no metric is available.

holding_count

Number of holdings assigned to the bucket.

class fuggers_py.portfolio.Bucketing(portfolio)

Convenience wrapper for portfolio bucketing helpers.

Parameters:

portfolio (Portfolio)

bucket_dv01(curve, settlement_date, buckets=(('0-2Y', 0.0, 2.0), ('2-5Y', 2.0, 5.0), ('5-10Y', 5.0, 10.0), ('10Y+', 10.0, None)))

Return maturity buckets with PV-weighted averages.

Each bucket is keyed by the supplied maturity definition and includes clean PV, dirty PV, DV01, and dirty-value-weighted averages.

Return type:

list[BucketResult]

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

fuggers_py.portfolio.bucket_by_classifier(portfolio, classifier_name)

Bucket holdings by a named classifier or custom field.

Return type:

ClassifierDistribution

Parameters:
  • portfolio (Portfolio)

  • classifier_name (str)

fuggers_py.portfolio.bucket_by_country(portfolio)

Bucket holdings by country.

Return type:

dict[str, list[Holding]]

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_currency(portfolio)

Bucket holdings by currency.

Return type:

dict[str, list[Holding]]

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_custom_field(portfolio, field_name)

Bucket holdings by a custom field name.

Return type:

CustomDistribution

Parameters:
fuggers_py.portfolio.bucket_by_issuer(portfolio)

Bucket holdings by issuer.

Return type:

dict[str, list[Holding]]

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_maturity(portfolio, *, settlement_date, buckets=(('0-2Y', 0.0, 2.0), ('2-5Y', 2.0, 5.0), ('5-10Y', 5.0, 10.0), ('10Y+', 10.0, None)))

Bucket holdings by time to maturity in years.

Return type:

MaturityDistribution

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_rating(portfolio)

Bucket holdings by credit rating.

Return type:

RatingDistribution

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_region(portfolio)

Bucket holdings by region.

Return type:

dict[str, list[Holding]]

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.bucket_by_sector(portfolio)

Bucket holdings by sector.

Return type:

SectorDistribution

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_attribution(portfolio, *, curve, settlement_date)

Compatibility alias for attribution_summary().

Return type:

PortfolioAttribution

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_credit_metrics(portfolio)

Compatibility alias for calculate_credit_quality().

Return type:

CreditQualityMetrics

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_credit_quality(portfolio)

Return portfolio credit-quality metrics with typed risk fields.

Return type:

CreditQualityMetrics

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_distribution_yield(annual_distribution, market_price)

Return distribution yield as decimal, percent, and basis points.

Return type:

DistributionYield

Parameters:
  • annual_distribution (Decimal)

  • market_price (Decimal)

fuggers_py.portfolio.calculate_etf_nav(portfolio, *, curve, settlement_date, shares_outstanding=Decimal('1'), liabilities=Decimal('0'))

Return ETF NAV per share.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

  • liabilities (Decimal)

fuggers_py.portfolio.calculate_etf_nav_metrics(portfolio, *, curve, settlement_date, shares_outstanding=Decimal('1'), liabilities=Decimal('0'), market_price=None)

Return the full ETF NAV and per-share risk summary.

The output includes total NAV, NAV per share, indicative NAV, per-share DV01 and CS01, and optional premium/discount data when a market price is supplied.

Return type:

EtfNavMetrics

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

  • liabilities (Decimal)

  • market_price (Decimal | None)

fuggers_py.portfolio.calculate_inav(portfolio, *, curve, settlement_date, shares_outstanding=Decimal('1'))

Return ETF indicative NAV per share.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

fuggers_py.portfolio.calculate_liquidity_metrics(portfolio, *, curve, settlement_date)

Return the standard liquidity metrics.

Return type:

LiquidityMetrics

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_migration_risk(portfolio)

Return the BBB and BB crossover risk for the portfolio.

Return type:

MigrationRisk

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.calculate_nav_breakdown(portfolio, *, curve, settlement_date)

Return the portfolio NAV split into clean, dirty, and cash components.

Parameters:
  • portfolio (Portfolio) – Portfolio to aggregate.

  • curve – Yield curve input used to price the holdings.

  • settlement_date – Valuation date used for the portfolio metrics.

Returns:

Currency-unit split of clean PV, dirty PV, accrued interest, market value, dirty market value, and cash value.

Return type:

NavBreakdown

fuggers_py.portfolio.calculate_portfolio_analytics(portfolio, *, curve, settlement_date, config=None)

Return the full portfolio analytics summary.

The returned object includes the portfolio-level metrics computed by PortfolioAnalytics.

Parameters:
fuggers_py.portfolio.calculate_risk_metrics(portfolio, *, curve, settlement_date)

Return the standard portfolio rate-risk metrics.

Return type:

RiskMetrics

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.calculate_sec_yield(input_data, net_assets=None)

Return either standardized SEC yield or the legacy approximation.

Return type:

SecYield | Decimal

Parameters:
  • input_data (SecYieldInput | Decimal)

  • net_assets (Decimal | None)

fuggers_py.portfolio.calculate_spread_metrics(portfolio, *, curve, settlement_date)

Return the standard portfolio spread metrics.

Return type:

SpreadMetrics

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.calculate_yield_metrics(portfolio, *, curve, settlement_date)

Return the standard portfolio yield metrics.

Return type:

YieldMetrics

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

class fuggers_py.portfolio.CashPosition(amount, currency, label='cash', fx_rate=Decimal('1'))

A cash holding denominated in a portfolio currency.

The amount is treated as the position’s market value. The optional FX rate is only used when translating the cash amount into the portfolio’s base currency.

Parameters:
  • amount (Decimal)

  • currency (Currency)

  • label (str)

  • fx_rate (Decimal)

market_value()

Return the cash amount as market value.

Return type:

Decimal

property base_currency_value: Decimal

Return the cash amount translated by fx_rate.

class fuggers_py.portfolio.Classification(sector=None, rating=None, seniority=None, country=None, currency=None, issuer=None, region=None, custom_fields=<factory>)

Optional cross-sectional classification data for a holding.

The fields are used for bucketing, active-weight analysis, and custom portfolio aggregation. A field may be left unset when the information is not available at holding creation time.

Parameters:
  • sector (Sector | None)

  • rating (CreditRating | None)

  • seniority (Seniority | None)

  • country (str | None)

  • currency (Currency | None)

  • issuer (str | None)

  • region (str | None)

  • custom_fields (dict[str, str])

class fuggers_py.portfolio.ClassifierDistribution(classifier_name, entries)

Distribution keyed by a named classification dimension.

Parameters:
  • classifier_name (str)

  • entries (dict[str, list[object]])

fuggers_py.portfolio.compare_portfolios(portfolio, benchmark, curve, settlement_date)

Compare a portfolio with a benchmark on risk, yield, and spread.

Return type:

BenchmarkComparison

Parameters:
  • portfolio (Portfolio)

  • benchmark (Portfolio)

  • curve (DiscountingCurve)

  • settlement_date (Date)

class fuggers_py.portfolio.ComplianceCheck(name, passed, value, limit, description, severity)

Single ETF compliance check result.

Parameters:
  • name (str)

  • passed (bool)

  • value (Decimal)

  • limit (Decimal)

  • description (str)

  • severity (ComplianceSeverity)

class fuggers_py.portfolio.ComplianceSeverity(value)

Severity of an ETF compliance check.

class fuggers_py.portfolio.Contribution(portfolio)

Convenience wrapper for portfolio contribution helpers.

Parameters:

portfolio (Portfolio)

by_position(curve, settlement_date)

Return holding-level attribution for the portfolio.

aggregate(curve, settlement_date, *, assumptions=None)

Return aggregated contribution and return decomposition.

Parameters:

assumptions (AttributionInput | None)

class fuggers_py.portfolio.CreationBasket(components, flow_summary)

Ordered ETF creation basket and its flow summary.

Parameters:
by_name(name)

Return the basket component with name if present.

Return type:

BasketComponent | None

Parameters:

name (str)

property component_count: int

Return the number of basket components.

property basket_per_share: Decimal

Return the basket value per ETF share.

fuggers_py.portfolio.CreditMetrics

alias of CreditQualityMetrics

class fuggers_py.portfolio.CreditQualityMetrics(distribution, sector_distribution, average_score, average_rating, investment_grade_weight, high_yield_weight, default_weight, unrated_weight, bbb_weight, bb_weight, crossover_weight, quality_tiers, migration_risk)

Portfolio credit-quality distribution and migration risk.

Parameters:
  • distribution (dict[str, Decimal])

  • sector_distribution (dict[str, Decimal])

  • average_score (Decimal)

  • average_rating (object)

  • investment_grade_weight (Decimal)

  • high_yield_weight (Decimal)

  • default_weight (Decimal)

  • unrated_weight (Decimal)

  • bbb_weight (Decimal)

  • bb_weight (Decimal)

  • crossover_weight (Decimal)

  • quality_tiers (QualityTiers)

  • migration_risk (MigrationRisk)

class fuggers_py.portfolio.CreditRating(value)

Issuer credit rating buckets.

score()

Return an ordinal score where lower numbers indicate stronger credit.

Return type:

int

class fuggers_py.portfolio.Cs01Contributions(entries, total=Decimal('0'))

Holding CS01 contributions.

Parameters:
fuggers_py.portfolio.cs01_contributions(portfolio, *, curve, settlement_date)

Compatibility alias for spread_contributions().

Return type:

Cs01Contributions

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.cs01_per_share(portfolio, *, curve, settlement_date, shares_outstanding=Decimal('1'))

Return ETF CS01 per share in currency units per 1 bp.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

class fuggers_py.portfolio.CustomDistribution(field_name, entries)

Distribution keyed by a user-defined field name.

Parameters:
  • field_name (str)

  • entries (dict[str, list[object]])

class fuggers_py.portfolio.DaysToLiquidate(days, liquidity_score, liquidation_fraction=Decimal('1'))

Estimated days to liquidate at a given liquidation fraction.

Parameters:
  • days (Decimal)

  • liquidity_score (Decimal)

  • liquidation_fraction (Decimal)

class fuggers_py.portfolio.DistributionYield(distribution_yield, annual_distribution, market_price, distribution_yield_pct, distribution_yield_bps)

Distribution yield expressed as raw decimal, percent, and bps.

Parameters:
  • distribution_yield (Decimal)

  • annual_distribution (Decimal)

  • market_price (Decimal)

  • distribution_yield_pct (Decimal)

  • distribution_yield_bps (Decimal)

property yield_pct: Decimal

Return the distribution yield in percentage points.

as_decimal()

Return the raw decimal distribution yield.

Return type:

Decimal

fuggers_py.portfolio.duration_contributions(portfolio, *, curve, settlement_date)

Return duration contributions weighted by dirty PV.

Each holding contribution is its duration multiplied by its dirty-value weight in the portfolio.

Return type:

DurationContributions

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.duration_difference_by_sector(portfolio, benchmark, *, curve, settlement_date)

Return sector-level duration differences versus benchmark.

Return type:

SectorAttribution

Parameters:
class fuggers_py.portfolio.DurationComparison(portfolio_duration, benchmark_duration, active_duration)

Portfolio, benchmark, and active duration values.

Parameters:
  • portfolio_duration (Decimal)

  • benchmark_duration (Decimal)

  • active_duration (Decimal)

class fuggers_py.portfolio.DurationContributions(entries, total=Decimal('0'))

Holding duration contributions.

Parameters:
fuggers_py.portfolio.dv01_contributions(portfolio, *, curve, settlement_date)

Return holding DV01 contributions.

The contribution amount is the holding-level DV01 returned by the analytics layer.

Return type:

Dv01Contributions

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.dv01_per_share(portfolio, *, curve, settlement_date, shares_outstanding=Decimal('1'))

Return ETF DV01 per share in currency units per 1 bp.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • shares_outstanding (Decimal)

class fuggers_py.portfolio.Dv01Contributions(entries, total=Decimal('0'))

Holding DV01 contributions.

Parameters:
class fuggers_py.portfolio.EtfPricer

Aggregate bond quote outputs into ETF-style analytics.

price(etf_id, holdings, quote_outputs, *, shares_outstanding)

Aggregate holding-level outputs into an ETF analytics record.

The pricer sums value-weighted risk outputs across holdings and returns an ETF-style analytics record with NAV, per-share metrics, and aggregation counts.

Return type:

EtfAnalyticsOutput

Parameters:
  • etf_id (EtfId | str | None)

  • holdings (list[EtfHolding] | tuple[EtfHolding, ...])

  • quote_outputs (dict[InstrumentId, BondQuoteOutput])

  • shares_outstanding (Decimal)

fuggers_py.portfolio.estimate_days_to_liquidate(portfolio, *, curve, settlement_date, liquidation_fraction=Decimal('1'))

Estimate days to liquidate for the portfolio.

Return type:

DaysToLiquidate

Parameters:
  • portfolio (Portfolio)

  • liquidation_fraction (Decimal)

fuggers_py.portfolio.estimate_income_returns(portfolio, *, curve, settlement_date, horizon_years=None, assumptions=None)

Estimate income return over the provided horizon as a raw decimal.

The estimate is the portfolio current yield multiplied by the horizon in years.

Return type:

Decimal

Parameters:
fuggers_py.portfolio.estimate_rate_returns(portfolio, *, curve, settlement_date, rate_change_bps=None, assumptions=None)

Estimate rate return from a parallel move in raw decimal terms.

The result is a first-order estimate based on portfolio DV01 and the requested rate shock.

Return type:

Decimal

Parameters:
fuggers_py.portfolio.estimate_spread_returns(portfolio, *, curve, settlement_date, spread_change_bps=None, assumptions=None)

Estimate spread return from a spread move in raw decimal terms.

The result is a first-order estimate based on portfolio CS01 and the requested spread shock.

Return type:

Decimal

Parameters:
fuggers_py.portfolio.estimate_tracking_error(benchmark, curve, settlement_date)

Estimate tracking error from active risk and holding dispersion.

The result combines active duration, active spread, and a simple dispersion term derived from active weights.

Return type:

TrackingErrorEstimate

Parameters:
fuggers_py.portfolio.estimate_yield_from_holdings(portfolio, *, curve, settlement_date, gross_expense_ratio=Decimal('0'), fee_waiver_ratio=Decimal('0'))

Estimate gross and net yield after expenses from holdings.

The gross yield is based on the portfolio YTM, then adjusted by the input expense and fee-waiver ratios to produce a net-yield estimate.

Return type:

ExpenseMetrics

Parameters:
  • portfolio (Portfolio)

  • gross_expense_ratio (Decimal)

  • fee_waiver_ratio (Decimal)

class fuggers_py.portfolio.EtfComplianceReport(weights_sum_to_one, issuer_limit_ok, checks)

Summary of ETF compliance checks.

Parameters:
  • weights_sum_to_one (bool)

  • issuer_limit_ok (bool)

  • checks (tuple[ComplianceCheck, ...])

property passed: bool

Return True when all checks pass.

by_name(name)

Return the named compliance check if present.

Return type:

ComplianceCheck | None

Parameters:

name (str)

class fuggers_py.portfolio.EtfNavMetrics(total_nav, shares_outstanding, nav_per_share, dv01_per_share, cs01_per_share, securities_value, cash_value, accrued_interest, liabilities, inav=None, market_price=None, premium_discount=None, premium_discount_dollars=None)

ETF NAV, iNAV, and per-share risk metrics.

Parameters:
  • total_nav (Decimal)

  • shares_outstanding (Decimal)

  • nav_per_share (Decimal)

  • dv01_per_share (Decimal)

  • cs01_per_share (Decimal)

  • securities_value (Decimal)

  • cash_value (Decimal)

  • accrued_interest (Decimal)

  • liabilities (Decimal)

  • inav (Decimal | None)

  • market_price (Decimal | None)

  • premium_discount (PremiumDiscountStats | None)

  • premium_discount_dollars (Decimal | None)

property premium_discount_pct: Decimal | None

Return the premium or discount in percent.

property premium_discount_bps: Decimal | None

Return the premium or discount in basis points.

is_premium()

Return True when a premium/discount value is available and positive.

Return type:

bool

is_discount()

Return True when a premium/discount value is available and negative.

Return type:

bool

abs_premium_discount()

Return the absolute premium or discount in percent.

Return type:

Decimal | None

fuggers_py.portfolio.etf_compliance_checks(*, holdings_weight_sum, max_issuer_weight=None)

Evaluate a small set of ETF compliance checks.

The checks are intentionally simple: holdings weights should sum to one, and the issuer limit must stay within the provided bound when supplied.

Return type:

EtfComplianceReport

Parameters:
  • holdings_weight_sum (Decimal)

  • max_issuer_weight (Decimal | None)

class fuggers_py.portfolio.ExpenseMetrics(gross_yield, net_yield, gross_expense_ratio, net_expense_ratio, fee_waiver_ratio, annual_income_estimate, annual_expense_amount, net_assets)

Yield and expense metrics estimated from holdings.

Parameters:
  • gross_yield (Decimal)

  • net_yield (Decimal)

  • gross_expense_ratio (Decimal)

  • net_expense_ratio (Decimal)

  • fee_waiver_ratio (Decimal)

  • annual_income_estimate (Decimal)

  • annual_expense_amount (Decimal)

  • net_assets (Decimal)

property expense_drag: Decimal

Return the yield drag from expenses.

property yield_before_expenses: Decimal

Return the gross yield.

property yield_after_expenses: Decimal

Return the net yield.

class fuggers_py.portfolio.FallenAngelRisk(bbb_weight, market_value_at_risk, holdings_count)

Weight and market value at risk from BBB holdings.

Parameters:
  • bbb_weight (Decimal)

  • market_value_at_risk (Decimal)

  • holdings_count (int)

property weight: Decimal

Return the BBB weight.

class fuggers_py.portfolio.Holding(instrument, quantity=Decimal('1'), clean_price=None, market_value=None, accrued_interest=None, analytics=None, label=None, id=None, classification=None, rating_info=None, sector_info=None, seniority_info=None, liquidity_score=None, custom_fields=<factory>, fx_rate=Decimal('1'))

A bond holding with optional valuation and classification metadata.

The holding stores the instrument, position size, optional pricing inputs, and optional classification metadata used for portfolio aggregation. When a clean price is present, market value is derived from the price and quantity unless an explicit market value is provided.

Parameters:
  • instrument (Bond)

  • quantity (Decimal)

  • clean_price (Price | Decimal | None)

  • market_value (Decimal | None)

  • accrued_interest (Decimal | None)

  • analytics (HoldingAnalytics | None)

  • label (str | None)

  • id (str | None)

  • classification (Classification | None)

  • rating_info (RatingInfo | None)

  • sector_info (SectorInfo | None)

  • seniority_info (SeniorityInfo | None)

  • liquidity_score (Decimal | None)

  • custom_fields (dict[str, str])

  • fx_rate (Decimal)

property par_amount: Decimal

Return the par amount carried by the holding.

property market_price: Decimal | None

Return the clean market price as a percent-of-par value.

property currency: Currency

Return the holding currency, preferring explicit classification.

name()

Return a stable display name for the holding.

Return type:

str

property market_value_amount: Decimal

Return the clean market value in currency units.

If an explicit market value is present it is used directly; otherwise the clean price is multiplied by the par amount.

property dirty_market_value: Decimal

Return the dirty market value in currency units.

Accrued interest is added on a par-amount basis so the result is a dirty value consistent with dirty-PV aggregation.

property base_currency_value: Decimal

Return the clean market value translated by fx_rate.

weight_in_portfolio(total_market_value)

Return the clean-value weight of the holding within a portfolio.

Return type:

Decimal

Parameters:

total_market_value (Decimal)

class fuggers_py.portfolio.HoldingAnalytics(name, market_value, dirty_value, clean_value, accrued_value, duration, convexity, dv01, ytm=None, ytw=None, ytc=None, current_yield=None, best_yield=None, z_spread=None, oas=None, g_spread=None, i_spread=None, asw=None, best_spread=None, spread_duration=None, cs01=None, modified_duration=None, effective_duration=None, macaulay_duration=None, effective_convexity=None, key_rate_profile=<factory>, liquidity_score=None, weighted_average_life=None, coupon=None)

Per-holding analytics expressed in currency and raw-decimal units.

The currency fields are value terms for the individual holding. Yield, spread, duration, convexity, and key-rate fields use the library’s raw decimal convention unless the field name explicitly says otherwise.

Parameters:
  • name (str)

  • market_value (Decimal)

  • dirty_value (Decimal)

  • clean_value (Decimal)

  • accrued_value (Decimal)

  • duration (Decimal)

  • convexity (Decimal)

  • dv01 (Decimal)

  • ytm (Decimal | None)

  • ytw (Decimal | None)

  • ytc (Decimal | None)

  • current_yield (Decimal | None)

  • best_yield (Decimal | None)

  • z_spread (Decimal | None)

  • oas (Decimal | None)

  • g_spread (Decimal | None)

  • i_spread (Decimal | None)

  • asw (Decimal | None)

  • best_spread (Decimal | None)

  • spread_duration (Decimal | None)

  • cs01 (Decimal | None)

  • modified_duration (Decimal | None)

  • effective_duration (Decimal | None)

  • macaulay_duration (Decimal | None)

  • effective_convexity (Decimal | None)

  • key_rate_profile (dict[str, Decimal])

  • liquidity_score (Decimal | None)

  • weighted_average_life (Decimal | None)

  • coupon (Decimal | None)

class fuggers_py.portfolio.HoldingAttribution(name, pv_pct, dv01_pct, duration_contribution)

Holding-level PV and DV01 attribution.

Parameters:
  • name (str)

  • pv_pct (Decimal)

  • dv01_pct (Decimal)

  • duration_contribution (Decimal)

as_dict()

Return a mapping-style representation.

Return type:

dict[str, Decimal | str]

class fuggers_py.portfolio.HoldingBuilder(instrument=None, quantity=Decimal('1'), clean_price=None, market_value=None, accrued_interest=None, analytics=None, label=None, id=None, classification=None, rating_info=None, sector_info=None, seniority_info=None, liquidity_score=None, custom_fields=<factory>, fx_rate=Decimal('1'))

Mutable builder for Holding instances.

Parameters:
  • instrument (Bond | None)

  • quantity (Decimal)

  • clean_price (Price | Decimal | None)

  • market_value (Decimal | None)

  • accrued_interest (Decimal | None)

  • analytics (HoldingAnalytics | None)

  • label (str | None)

  • id (str | None)

  • classification (Classification | None)

  • rating_info (RatingInfo | None)

  • sector_info (SectorInfo | None)

  • seniority_info (SeniorityInfo | None)

  • liquidity_score (Decimal | None)

  • custom_fields (dict[str, str])

  • fx_rate (Decimal)

with_instrument(instrument)

Set the underlying instrument.

Return type:

HoldingBuilder

Parameters:

instrument (Bond)

with_quantity(quantity)

Set the par amount or share quantity.

Return type:

HoldingBuilder

Parameters:

quantity (object)

with_par_amount(par_amount)

Compatibility alias for with_quantity().

Return type:

HoldingBuilder

Parameters:

par_amount (object)

with_clean_price(clean_price)

Set the clean price as a percent-of-par value.

Return type:

HoldingBuilder

Parameters:

clean_price (Price | Decimal)

with_market_price(market_price)

Compatibility alias for with_clean_price().

Return type:

HoldingBuilder

Parameters:

market_price (Price | Decimal)

with_market_value(market_value)

Set the clean market value in currency units.

Return type:

HoldingBuilder

Parameters:

market_value (object)

with_accrued_interest(accrued_interest)

Set accrued interest in currency units.

Return type:

HoldingBuilder

Parameters:

accrued_interest (object)

with_analytics(analytics)

Attach precomputed analytics.

Return type:

HoldingBuilder

Parameters:

analytics (HoldingAnalytics)

with_label(label)

Set the display label.

Return type:

HoldingBuilder

Parameters:

label (str)

with_id(value)

Set the stable identifier used as a fallback name.

Return type:

HoldingBuilder

Parameters:

value (str)

with_classification(classification)

Attach classification metadata.

Return type:

HoldingBuilder

Parameters:

classification (Classification)

with_rating_info(rating_info)

Attach rating metadata.

Return type:

HoldingBuilder

Parameters:

rating_info (RatingInfo)

with_sector_info(sector_info)

Attach sector metadata.

Return type:

HoldingBuilder

Parameters:

sector_info (SectorInfo)

with_seniority_info(seniority_info)

Attach seniority metadata.

Return type:

HoldingBuilder

Parameters:

seniority_info (SeniorityInfo)

with_liquidity_score(liquidity_score)

Set the liquidity score as a raw decimal.

Return type:

HoldingBuilder

Parameters:

liquidity_score (object)

with_fx_rate(fx_rate)

Set the base-currency FX conversion rate.

Return type:

HoldingBuilder

Parameters:

fx_rate (object)

build()

Create the immutable holding.

Raises:

ValueError – If no instrument has been set.

Return type:

Holding

class fuggers_py.portfolio.HoldingContribution(name, amount, metric, value_key, weight=Decimal('0'))

Contribution for a single holding.

Parameters:
  • name (str)

  • amount (Decimal)

  • metric (str)

  • value_key (str)

  • weight (Decimal)

property contribution: Decimal

Return the raw contribution amount.

property duration_contribution: Decimal

Return the duration contribution when this metric is duration.

property dv01_contribution: Decimal

Return the DV01 contribution when this metric is DV01.

property spread_contribution: Decimal

Return the spread contribution when this metric is CS01.

property cs01_contribution: Decimal

Return the CS01 contribution alias.

as_dict()

Return a mapping-style representation.

Return type:

dict[str, Decimal | str]

class fuggers_py.portfolio.KeyRateProfile(entries)

Key-rate DV01 profile keyed by tenor string.

Parameters:

entries (dict[str, Decimal])

property total_dv01: Decimal

Return the sum of all tenor contributions.

by_tenor(tenor)

Return the contribution for tenor if present.

Return type:

Decimal | None

Parameters:

tenor (str)

fuggers_py.portfolio.key_rate_shift_impact(portfolio, *, curve, settlement_date, tenor_shocks_bps)

Return the dirty-PV change for tenor-specific key-rate shocks.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • tenor_shocks_bps (dict[str, Decimal])

fuggers_py.portfolio.key_rate_shift_result(portfolio, *, curve, settlement_date, tenor_shocks_bps, scenario_name=None)

Return a typed result for a key-rate shift scenario.

Return type:

StressResult

Parameters:
  • portfolio (Portfolio)

  • tenor_shocks_bps (dict[str, Decimal])

  • scenario_name (str | None)

class fuggers_py.portfolio.KeyRateShiftScenario(name, tenor_shocks_bps)

Collection of tenor-specific basis-point shocks.

Parameters:
  • name (str)

  • tenor_shocks_bps (dict[str, Decimal])

property tenor_shifts: tuple[TenorShift, ...]

Return the tenor shocks as typed objects.

classmethod from_tenor_shifts(name, shifts)

Build a scenario from typed tenor shifts.

Return type:

KeyRateShiftScenario

Parameters:
fuggers_py.portfolio.liquidity_distribution(portfolio, *, curve, settlement_date)

Return the liquidity bucket distribution.

Return type:

LiquidityDistribution

Parameters:

portfolio (Portfolio)

class fuggers_py.portfolio.LiquidityBucket(label, min_score, max_score, weight, dirty_pv, holding_count)

A liquidity-score bucket with dirty-PV weight.

Parameters:
  • label (str)

  • min_score (Decimal)

  • max_score (Decimal | None)

  • weight (Decimal)

  • dirty_pv (Decimal)

  • holding_count (int)

class fuggers_py.portfolio.LiquidityDistribution(entries)

Ordered liquidity buckets keyed by label.

Parameters:

entries (tuple[LiquidityBucket, ...])

keys()
Return type:

tuple[str, ...]

values()
Return type:

tuple[LiquidityBucket, ...]

items()
Return type:

tuple[tuple[str, LiquidityBucket], ...]

property total_weight: Decimal

Return the sum of bucket weights.

property total_dirty_pv: Decimal

Return the sum of bucket dirty PV.

class fuggers_py.portfolio.LiquidityMetrics(liquidity_score, bid_ask_spread, days_to_liquidate, distribution)

Portfolio liquidity summary and bucket distribution.

Parameters:
class fuggers_py.portfolio.MaturityBucket(label, start_years, end_years)

A half-open maturity bucket measured in years.

Parameters:
  • label (str)

  • start_years (float)

  • end_years (float | None)

contains(years_to_maturity)

Return True when years_to_maturity falls inside the bucket.

Return type:

bool

Parameters:

years_to_maturity (float)

class fuggers_py.portfolio.MaturityDistribution(entries, bucket_definition)

Distribution keyed by maturity bucket labels.

Parameters:
  • entries (dict[str, list[object]])

  • bucket_definition (tuple[tuple[str, float, float | None], ...])

class fuggers_py.portfolio.MigrationRisk(fallen_angel_risk, rising_star_risk, fallen_angel, rising_star)

Combined fallen-angel and rising-star migration risk.

Parameters:
property crossover_weight: Decimal

Return the combined BBB and BB crossover weight.

class fuggers_py.portfolio.NavBreakdown(clean_pv, dirty_pv, accrued, market_value, dirty_market_value, cash_value)

Portfolio NAV components in currency units.

Parameters:
  • clean_pv (Decimal)

  • dirty_pv (Decimal)

  • accrued (Decimal)

  • market_value (Decimal)

  • dirty_market_value (Decimal)

  • cash_value (Decimal)

fuggers_py.portfolio.parallel_shift_impact(portfolio, *, curve, settlement_date, bump_bps, scenario_name=None)

Compatibility alias for rate_shock_impact().

Return type:

StressResult

Parameters:
  • portfolio (Portfolio)

  • bump_bps (Decimal)

  • scenario_name (str | None)

fuggers_py.portfolio.partial_dv01s(portfolio, *, curve, settlement_date)

Return the portfolio key-rate DV01 profile.

This is a compatibility alias for aggregate_key_rate_profile().

Return type:

KeyRateProfile

Parameters:

portfolio (Portfolio)

class fuggers_py.portfolio.Portfolio(positions, currency)

A currency-denominated collection of positions and cash.

Parameters:
positions

The portfolio holdings, stored in input order.

currency

Base reporting currency for portfolio-level analytics.

classmethod new(positions, currency)

Build a portfolio from a mutable position list.

The input list is converted to an immutable tuple so later analytics see a stable position order.

Return type:

Portfolio

Parameters:
total_quantity()

Return the sum of quantity-bearing position quantities.

Cash positions are ignored because they do not carry a par quantity.

Return type:

Decimal

holdings()

Return the raw holdings tuple, including cash positions.

Return type:

tuple[Holding | CashPosition, ...]

investable_holdings()

Return the bond holdings, excluding cash positions.

Return type:

tuple[Holding, ...]

cash_positions()

Return the cash positions in the portfolio.

Return type:

tuple[CashPosition, ...]

total_market_value()

Return total market value in the portfolio’s base currency units.

Bond holdings contribute clean market value while cash contributes its face amount. The result is therefore a clean, portfolio-level value proxy rather than a dirty PV.

Return type:

Decimal

class fuggers_py.portfolio.PortfolioAnalytics(portfolio)

Evaluate portfolio holdings into typed analytics outputs.

The object is a thin wrapper around the portfolio plus the analytics helpers that derive per-position and portfolio-level metrics.

Parameters:

portfolio (Portfolio)

position_metrics(curve, settlement_date, *, config=None, spread_curve=None, oas_calculator=None)

Return per-position analytics for the portfolio.

Parameters:
  • curve (DiscountingCurve | None) – Valuation curve used for discounting and risk calculations.

  • settlement_date (Date) – Settlement or valuation date that anchors accrued interest and year-fraction conventions.

  • config (AnalyticsConfig | None) – Optional analytics configuration. When omitted, the portfolio currency and settlement date are used to build a default config.

  • spread_curve (DiscountingCurve | None) – Optional curve used for spread calculations when it differs from the valuation curve.

  • oas_calculator (OASCalculator | None) – Optional spread calculator override.

Returns:

One analytics record per position in portfolio order.

Return type:

list[PositionAnalytics]

metrics(curve, settlement_date, *, config=None, spread_curve=None, oas_calculator=None)

Return portfolio-level aggregated analytics.

The totals and weighted averages are reported in the portfolio’s base currency and raw-decimal risk units. The weighting basis follows the active analytics configuration.

Return type:

PortfolioMetrics

Parameters:
  • curve (DiscountingCurve | None)

  • settlement_date (Date)

  • config (AnalyticsConfig | None)

  • spread_curve (DiscountingCurve | None)

  • oas_calculator (OASCalculator | None)

class fuggers_py.portfolio.PortfolioAnalyzer

Aggregate bond quote outputs into portfolio analytics.

analyze(portfolio_id, positions, quote_outputs, *, reference_data=None)

Aggregate positions into portfolio-level market value and risk.

Holdings without a quote are skipped. Risk metrics are value weighted by dirty value, and the output includes sector and rating breakdowns when reference data is supplied.

Return type:

PortfolioAnalyticsOutput

Parameters:
  • portfolio_id (PortfolioId | str | None)

  • positions (list[PortfolioPosition | EtfHolding] | tuple[PortfolioPosition | EtfHolding, ...])

  • quote_outputs (dict[InstrumentId, BondQuoteOutput])

  • reference_data (dict[InstrumentId, _ReferenceDataLike] | None)

class fuggers_py.portfolio.PortfolioAttribution(entries, total_pv_pct=Decimal('0'), total_dv01_pct=Decimal('0'), total_duration_contribution=Decimal('0'))

Sequence of holding-level attribution records.

Parameters:
  • entries (tuple[HoldingAttribution, ...])

  • total_pv_pct (Decimal)

  • total_dv01_pct (Decimal)

  • total_duration_contribution (Decimal)

by_name(name)

Return the named holding attribution if present.

Return type:

HoldingAttribution | None

Parameters:

name (str)

class fuggers_py.portfolio.PortfolioBenchmark(portfolio, benchmark)

Pair a portfolio with its benchmark for repeated comparisons.

Parameters:
compare(curve, settlement_date)

Return the portfolio-versus-benchmark comparison.

Return type:

BenchmarkComparison

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

active_weights(curve, settlement_date)

Return active holding weights.

Return type:

ActiveWeights

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

active_weights_by_holding(curve, settlement_date)

Return active holding weights with holding dimension metadata.

Return type:

ActiveWeights

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

active_weights_by_sector(curve, settlement_date)

Return active sector weights.

Return type:

ActiveWeights

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

active_weights_by_rating(curve, settlement_date)

Return active rating weights.

Return type:

ActiveWeights

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

aggregated_attribution(curve, settlement_date, *, assumptions=None)

Return aggregated attribution for the paired portfolio and benchmark.

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

duration_difference_by_sector(curve, settlement_date)

Return sector duration differences.

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

spread_difference_by_sector(curve, settlement_date)

Return sector spread differences.

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

overweight_underweight_counts(curve, settlement_date)

Return overweight and underweight active-weight counts.

Return type:

dict[str, int]

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

largest_active_positions(curve, settlement_date, *, limit=5)

Return the largest active positions by absolute active weight.

Return type:

list[tuple[str, Decimal]]

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

  • limit (int)

tracking_error_estimate(curve, settlement_date)

Return the heuristic tracking error estimate.

Return type:

Decimal

Parameters:
  • curve (DiscountingCurve)

  • settlement_date (Date)

class fuggers_py.portfolio.PortfolioBuilder(currency=None, _positions=<factory>)

Incrementally assemble a Portfolio.

Parameters:
with_currency(currency)

Set the portfolio reporting currency.

Return type:

PortfolioBuilder

Parameters:

currency (Currency)

add_position(position)

Append a position and infer currency from it if needed.

If the builder does not yet have a currency, it adopts the position’s currency on the first appended item.

Return type:

PortfolioBuilder

Parameters:

position (Holding | CashPosition)

add_holding(holding)

Compatibility alias for add_position().

Return type:

PortfolioBuilder

Parameters:

holding (Holding | CashPosition)

add_positions(positions)

Append multiple positions to the builder.

Return type:

PortfolioBuilder

Parameters:

positions (list[Holding | CashPosition])

build()

Create the immutable portfolio.

Raises:

ValueError – If no currency has been set or inferred.

Return type:

Portfolio

class fuggers_py.portfolio.PortfolioMetrics(clean_pv, dirty_pv, accrued, duration, convexity, dv01, weights, currency, current_yield=Decimal('0'), ytm=Decimal('0'), ytw=Decimal('0'), ytc=Decimal('0'), best_yield=Decimal('0'), z_spread=Decimal('0'), oas=Decimal('0'), g_spread=None, i_spread=None, asw=None, best_spread=Decimal('0'), spread_duration=Decimal('0'), cs01=Decimal('0'), liquidity_score=Decimal('0'), key_rate_profile=<factory>, total_market_value=Decimal('0'), total_dirty_market_value=Decimal('0'), total_accrued_interest=Decimal('0'), cash_value=Decimal('0'), holding_count=0, priced_count=0, coverage_count=0, modified_duration=Decimal('0'), effective_duration=Decimal('0'), macaulay_duration=Decimal('0'), effective_convexity=Decimal('0'), weighted_average_maturity=Decimal('0'), weighted_average_coupon=Decimal('0'))

Portfolio-level weighted metrics and totals.

All spread and rate values are raw decimals unless a name explicitly says bps or pct elsewhere in the API. PV and cash fields are currency amounts, while the weights and bucket shares are raw decimals.

Parameters:
  • clean_pv (Decimal)

  • dirty_pv (Decimal)

  • accrued (Decimal)

  • duration (Decimal)

  • convexity (Decimal)

  • dv01 (Decimal)

  • weights (dict[str, Decimal])

  • currency (Currency)

  • current_yield (Decimal)

  • ytm (Decimal)

  • ytw (Decimal)

  • ytc (Decimal)

  • best_yield (Decimal)

  • z_spread (Decimal)

  • oas (Decimal)

  • g_spread (Decimal | None)

  • i_spread (Decimal | None)

  • asw (Decimal | None)

  • best_spread (Decimal)

  • spread_duration (Decimal)

  • cs01 (Decimal)

  • liquidity_score (Decimal)

  • key_rate_profile (dict[str, Decimal])

  • total_market_value (Decimal)

  • total_dirty_market_value (Decimal)

  • total_accrued_interest (Decimal)

  • cash_value (Decimal)

  • holding_count (int)

  • priced_count (int)

  • coverage_count (int)

  • modified_duration (Decimal)

  • effective_duration (Decimal)

  • macaulay_duration (Decimal)

  • effective_convexity (Decimal)

  • weighted_average_maturity (Decimal)

  • weighted_average_coupon (Decimal)

class fuggers_py.portfolio.PortfolioPosition(instrument_id, quantity)

Quantity held in a single instrument.

Parameters:
  • instrument_id (InstrumentId)

  • quantity (Decimal)

fuggers_py.portfolio.Position

alias of Holding

fuggers_py.portfolio.PositionAnalytics

alias of HoldingAnalytics

class fuggers_py.portfolio.PremiumDiscountPoint(nav_per_share, market_price, shares_outstanding, premium_discount, premium_discount_dollars, estimated_edge_per_share, estimated_edge_bps, direction, is_actionable)

Premium/discount evaluation point with actionable edge data.

Parameters:
  • nav_per_share (Decimal)

  • market_price (Decimal)

  • shares_outstanding (Decimal)

  • premium_discount (PremiumDiscountStats)

  • premium_discount_dollars (Decimal)

  • estimated_edge_per_share (Decimal)

  • estimated_edge_bps (Decimal)

  • direction (str)

  • is_actionable (bool)

property premium_discount_pct: Decimal

Return the premium or discount in percent.

property premium_discount_bps: Decimal

Return the premium or discount in basis points.

class fuggers_py.portfolio.PremiumDiscountStats(premium_discount, premium_discount_bps, premium_discount_pct)

Portfolio premium or discount relative to NAV.

Parameters:
  • premium_discount (Decimal)

  • premium_discount_bps (Decimal)

  • premium_discount_pct (Decimal)

property premium_discount_dollars: Decimal

Return the per-share premium or discount in currency units.

property is_premium: bool

Return True when the market price is above NAV.

property is_discount: bool

Return True when the market price is below NAV.

fuggers_py.portfolio.premium_discount(nav, market_price)

Compatibility alias for premium_discount_stats().

Return type:

PremiumDiscountStats

Parameters:
  • nav (Decimal)

  • market_price (Decimal)

fuggers_py.portfolio.premium_discount_stats(nav, market_price)

Return ETF premium or discount statistics.

Return type:

PremiumDiscountStats

Parameters:
  • nav (Decimal)

  • market_price (Decimal)

class fuggers_py.portfolio.QualityTiers(investment_grade, high_yield, distressed, defaulted, unrated)

Normalized credit-quality tier weights.

Parameters:
  • investment_grade (Decimal)

  • high_yield (Decimal)

  • distressed (Decimal)

  • defaulted (Decimal)

  • unrated (Decimal)

fuggers_py.portfolio.RateScenario

alias of RateShockScenario

fuggers_py.portfolio.rate_shock_impact(portfolio, *, curve, settlement_date, bump_bps, scenario_name=None)

Return the dirty-PV change for a parallel rate shock in bps.

Return type:

StressResult

Parameters:
  • portfolio (Portfolio)

  • bump_bps (Decimal)

  • scenario_name (str | None)

class fuggers_py.portfolio.RateShockScenario(name, bump_bps)

Parallel rate shock measured in basis points.

Parameters:
  • name (str)

  • bump_bps (Decimal)

class fuggers_py.portfolio.RatingBucket(label, ratings)

A named set of credit ratings used for bucketing.

Parameters:
class fuggers_py.portfolio.RatingComparison(portfolio_weights, benchmark_weights, active_weights)

Portfolio, benchmark, and active rating weights.

Parameters:
  • portfolio_weights (dict[str, Decimal])

  • benchmark_weights (dict[str, Decimal])

  • active_weights (ActiveWeights)

class fuggers_py.portfolio.RatingDistribution(entries)

Distribution keyed by credit rating label.

Parameters:

entries (dict[str, list[object]])

class fuggers_py.portfolio.RatingInfo(rating, agency=None, outlook=None)

Rating metadata from an agency or internal source.

Parameters:
  • rating (CreditRating)

  • agency (str | None)

  • outlook (str | None)

class fuggers_py.portfolio.RiskComparison(portfolio_dirty_pv, benchmark_dirty_pv, active_dirty_pv, portfolio_dv01, benchmark_dv01, active_dv01)

Portfolio, benchmark, and active dirty PV and DV01 values.

Parameters:
  • portfolio_dirty_pv (Decimal)

  • benchmark_dirty_pv (Decimal)

  • active_dirty_pv (Decimal)

  • portfolio_dv01 (Decimal)

  • benchmark_dv01 (Decimal)

  • active_dv01 (Decimal)

class fuggers_py.portfolio.RiskMetrics(duration, modified_duration, effective_duration, macaulay_duration, best_duration, convexity, effective_convexity, dv01, cs01)

Portfolio rate-risk metrics expressed in raw-decimal units.

Parameters:
  • duration (Decimal)

  • modified_duration (Decimal)

  • effective_duration (Decimal)

  • macaulay_duration (Decimal)

  • best_duration (Decimal)

  • convexity (Decimal)

  • effective_convexity (Decimal)

  • dv01 (Decimal)

  • cs01 (Decimal)

class fuggers_py.portfolio.RisingStarRisk(bb_weight, market_value_potential, holdings_count)

Weight and market value potential from BB holdings.

Parameters:
  • bb_weight (Decimal)

  • market_value_potential (Decimal)

  • holdings_count (int)

property weight: Decimal

Return the BB weight.

fuggers_py.portfolio.run_stress_scenario(portfolio, *, curve, settlement_date, scenario)

Run a single stress scenario and return its result.

Return type:

StressResult

Parameters:
fuggers_py.portfolio.run_stress_scenarios(portfolio, *, curve, settlement_date, scenarios)

Run a list of stress scenarios and return a summary.

Return type:

StressSummary

Parameters:
  • portfolio (Portfolio)

  • scenarios (list[object])

class fuggers_py.portfolio.Sector(value)

High-level issuer sector classification.

class fuggers_py.portfolio.SectorAttribution(entries, metric, portfolio_total, benchmark_total, active_total)

Bucketed attribution results keyed by sector.

Parameters:
  • entries (tuple[BucketContribution, ...])

  • metric (str)

  • portfolio_total (Decimal)

  • benchmark_total (Decimal)

  • active_total (Decimal)

keys()
Return type:

tuple[str, ...]

values()
Return type:

tuple[BucketContribution, ...]

items()
Return type:

tuple[tuple[str, BucketContribution], ...]

property total_active: Decimal

Return the total active contribution.

class fuggers_py.portfolio.SectorComparison(portfolio_weights, benchmark_weights, active_weights)

Portfolio, benchmark, and active sector weights.

Parameters:
  • portfolio_weights (dict[str, Decimal])

  • benchmark_weights (dict[str, Decimal])

  • active_weights (ActiveWeights)

class fuggers_py.portfolio.SectorDistribution(entries)

Distribution keyed by sector label.

Parameters:

entries (dict[str, list[object]])

class fuggers_py.portfolio.SectorInfo(sector, issuer=None, country=None, region=None, subsector=None)

Sector metadata attached to a portfolio holding or issuer.

Parameters:
  • sector (Sector)

  • issuer (str | None)

  • country (str | None)

  • region (str | None)

  • subsector (str | None)

class fuggers_py.portfolio.SecYield(sec_30_day_yield, unsubsidized_yield, net_investment_income, average_shares_outstanding, max_offering_price, gross_expenses=None, fee_waivers=None, as_of_date=None)

Standardized SEC yield output and related inputs.

Parameters:
  • sec_30_day_yield (Decimal)

  • unsubsidized_yield (Decimal | None)

  • net_investment_income (Decimal)

  • average_shares_outstanding (Decimal)

  • max_offering_price (Decimal)

  • gross_expenses (Decimal | None)

  • fee_waivers (Decimal | None)

  • as_of_date (Date | None)

fee_waiver_impact()

Return the yield impact of fee waivers when available.

Return type:

Decimal | None

class fuggers_py.portfolio.SecYieldInput(net_investment_income, average_shares_outstanding, max_offering_price, gross_expenses=None, fee_waivers=None, as_of_date=None)

Inputs for the standardized SEC-yield calculation.

Parameters:
  • net_investment_income (Decimal)

  • average_shares_outstanding (Decimal)

  • max_offering_price (Decimal)

  • gross_expenses (Decimal | None)

  • fee_waivers (Decimal | None)

  • as_of_date (Date | None)

class fuggers_py.portfolio.Seniority(value)

Issuer seniority classification.

class fuggers_py.portfolio.SeniorityInfo(seniority, secured=False)

Seniority metadata attached to a portfolio holding or issuer.

Parameters:
class fuggers_py.portfolio.SpreadComparison(portfolio_z_spread, benchmark_z_spread, active_z_spread, portfolio_oas, benchmark_oas, active_oas)

Portfolio, benchmark, and active spread metrics.

Parameters:
  • portfolio_z_spread (Decimal)

  • benchmark_z_spread (Decimal)

  • active_z_spread (Decimal)

  • portfolio_oas (Decimal)

  • benchmark_oas (Decimal)

  • active_oas (Decimal)

fuggers_py.portfolio.spread_contributions(portfolio, *, curve, settlement_date)

Return holding CS01 contributions.

The contribution amount is the holding-level CS01 returned by the analytics layer.

Return type:

Cs01Contributions

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.spread_difference_by_sector(portfolio, benchmark, *, curve, settlement_date)

Return sector-level spread differences versus benchmark.

Return type:

SectorAttribution

Parameters:
fuggers_py.portfolio.spread_shock_impact(portfolio, *, curve, settlement_date, bump_bps)

Return the dirty-PV change for a parallel spread shock in bps.

Return type:

Decimal

Parameters:
fuggers_py.portfolio.spread_shock_result(portfolio, *, curve, settlement_date, bump_bps, scenario_name=None)

Return a typed result for a parallel spread shock.

Return type:

StressResult

Parameters:
  • portfolio (Portfolio)

  • bump_bps (Decimal)

  • scenario_name (str | None)

fuggers_py.portfolio.SpreadContributions

alias of Cs01Contributions

class fuggers_py.portfolio.SpreadMetrics(z_spread, oas, g_spread, i_spread, asw, best_spread, spread_duration, cs01)

Portfolio spread metrics expressed as raw decimals.

Parameters:
  • z_spread (Decimal)

  • oas (Decimal)

  • g_spread (Decimal | None)

  • i_spread (Decimal | None)

  • asw (Decimal | None)

  • best_spread (Decimal)

  • spread_duration (Decimal)

  • cs01 (Decimal)

fuggers_py.portfolio.SpreadScenario

alias of SpreadShockScenario

class fuggers_py.portfolio.SpreadShockScenario(name, bump_bps)

Parallel spread shock measured in basis points.

Parameters:
  • name (str)

  • bump_bps (Decimal)

fuggers_py.portfolio.standard_scenarios()

Return the standard set of illustrative stress scenarios.

Return type:

list[StressScenario]

class fuggers_py.portfolio.Stress(portfolio)

Convenience wrapper around portfolio stress helpers.

Parameters:

portfolio (Portfolio)

parallel_shift(curve, settlement_date, *, bump_bps)

Return the dirty-PV change for a parallel rate shift in bps.

fuggers_py.portfolio.stress_scenarios(portfolio, *, curve, settlement_date, scenarios)

Compatibility alias for run_stress_scenarios().

Return type:

StressSummary

Parameters:
  • portfolio (Portfolio)

  • scenarios (list[object])

class fuggers_py.portfolio.StressResult(base_dirty_pv, stressed_dirty_pv, actual_change, dv01_approximation, scenario_name=None, breakdown=<factory>)

Result of a stress scenario against portfolio dirty PV.

Parameters:
  • base_dirty_pv (Decimal)

  • stressed_dirty_pv (Decimal)

  • actual_change (Decimal)

  • dv01_approximation (Decimal)

  • scenario_name (str | None)

  • breakdown (dict[str, Decimal])

base_dirty_pv

Unstressed dirty present value.

stressed_dirty_pv

Dirty present value after applying the scenario.

actual_change

Signed PV change, where negative values indicate a loss.

dv01_approximation

First-order approximation of the change, in the same currency units.

scenario_name

Optional label for the scenario.

breakdown

Optional per-position or per-tenor change breakdown.

class fuggers_py.portfolio.StressScenario(name)

Base class for a named stress scenario.

Parameters:

name (str)

class fuggers_py.portfolio.StressSummary(results)

Mapping of scenario names to stress results.

Parameters:

results (dict[str, StressResult])

classmethod from_results(results)

Normalize a collection of stress results into a summary.

Return type:

StressSummary

Parameters:

results (StressSummary | Mapping[str, StressResult] | Iterable[StressResult])

property scenario_count: int

Return the number of scenarios in the summary.

property aggregate_change: Decimal

Return the sum of all scenario PV changes.

property worst_loss: Decimal

Return the most negative PV change.

property best_gain: Decimal

Return the most positive PV change.

best_case()

Return the scenario with the largest PV change.

Return type:

StressResult | None

worst_case()

Return the scenario with the smallest PV change.

Return type:

StressResult | None

fuggers_py.portfolio.summarize_results(results)

Normalize stress results into a StressSummary.

Return type:

StressSummary

Parameters:

results (StressSummary | Mapping[str, StressResult] | Iterable[StressResult])

class fuggers_py.portfolio.TenorShift(tenor, bump_bps)

A single key-rate tenor shock in basis points.

Parameters:
  • tenor (str)

  • bump_bps (Decimal)

fuggers_py.portfolio.total_cs01(portfolio, *, curve, settlement_date)

Return portfolio CS01 in currency units per 1 bp.

The value is the sum of holding-level CS01 contributions across the portfolio.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.total_dv01(portfolio, *, curve, settlement_date)

Return portfolio DV01 in currency units per 1 bp.

The value is the sum of holding-level DV01 contributions across the portfolio.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

class fuggers_py.portfolio.TrackingErrorEstimate(estimate, duration_component=Decimal('0'), spread_component=Decimal('0'), dispersion_component=Decimal('0'))

Heuristic tracking-error estimate and its components.

The estimate is a heuristic decimal value, not a statistically fitted tracking-error model.

Parameters:
  • estimate (Decimal)

  • duration_component (Decimal)

  • spread_component (Decimal)

  • dispersion_component (Decimal)

as_decimal()

Return the tracking-error estimate as a decimal.

Return type:

Decimal

fuggers_py.portfolio.active_weights(portfolio, benchmark, curve, settlement_date)

Return active holding weights for portfolio minus benchmark.

Return type:

ActiveWeights

Parameters:
  • portfolio (Portfolio)

  • benchmark (Portfolio)

  • curve (DiscountingCurve)

  • settlement_date (Date)

fuggers_py.portfolio.aggregated_attribution(portfolio, *, curve, settlement_date, assumptions=None, benchmark=None)

Return aggregated income, rate, and spread attribution.

When a benchmark is supplied, active attribution fields are populated as portfolio minus benchmark.

Return type:

AggregatedAttribution

Parameters:
fuggers_py.portfolio.benchmark_comparison(portfolio, benchmark, curve, settlement_date)

Compatibility alias for compare_portfolios().

Return type:

BenchmarkComparison

Parameters:
  • portfolio (Portfolio)

  • benchmark (Portfolio)

  • curve (DiscountingCurve)

  • settlement_date (Date)

fuggers_py.portfolio.top_contributors(contributions, *, value_key, limit=5, absolute=False)

Return the largest contributors ordered by the requested value key.

Return type:

list[HoldingContribution | Mapping[str, Decimal | str]]

Parameters:
  • contributions (Sequence[HoldingContribution] | Sequence[Mapping[str, Decimal | str]])

  • value_key (str)

  • limit (int)

  • absolute (bool)

fuggers_py.portfolio.weighted_asw(portfolio, *, curve, settlement_date)

Return the portfolio asset-swap spread as a raw decimal.

Return type:

Decimal | None

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_best_duration(portfolio, *, curve, settlement_date)

Return the dirty-value-weighted best duration.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_best_spread(portfolio, *, curve, settlement_date)

Return the portfolio best spread as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_best_yield(portfolio, *, curve, settlement_date)

Return the portfolio best yield as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_bid_ask_spread(portfolio, *, curve, settlement_date)

Return the dirty-value-weighted bid/ask spread as a raw decimal.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_convexity(portfolio, *, curve, settlement_date)

Return portfolio convexity as a raw decimal.

The result is the portfolio-level convexity from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_current_yield(portfolio, *, curve, settlement_date)

Return the portfolio current yield as a raw decimal.

The result is the portfolio-level current yield from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_duration(portfolio, *, curve, settlement_date)

Return portfolio duration as a raw decimal.

The result is the portfolio-level duration from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_effective_convexity(portfolio, *, curve, settlement_date)

Return the portfolio effective convexity as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_effective_duration(portfolio, *, curve, settlement_date)

Return the portfolio effective duration as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_g_spread(portfolio, *, curve, settlement_date)

Return the portfolio G-spread as a raw decimal.

Return type:

Decimal | None

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_i_spread(portfolio, *, curve, settlement_date)

Return the portfolio I-spread as a raw decimal.

Return type:

Decimal | None

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_liquidity_score(portfolio, *, curve, settlement_date)

Return the portfolio liquidity score as a raw decimal.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_macaulay_duration(portfolio, *, curve, settlement_date)

Return the portfolio Macaulay duration as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_modified_duration(portfolio, *, curve, settlement_date)

Return the portfolio modified duration as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_oas(portfolio, *, curve, settlement_date)

Return the portfolio OAS as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_spread_duration(portfolio, *, curve, settlement_date)

Return the portfolio spread duration as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

fuggers_py.portfolio.weighted_spreads(portfolio, *, curve, settlement_date)

Return the portfolio Z-spread as a raw decimal.

The result is the portfolio-level Z-spread from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_ytc(portfolio, *, curve, settlement_date)

Return the portfolio yield to call as a raw decimal.

The result is the portfolio-level yield to call from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_ytm(portfolio, *, curve, settlement_date)

Return the portfolio yield to maturity as a raw decimal.

The result is the portfolio-level yield to maturity from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_ytw(portfolio, *, curve, settlement_date)

Return the portfolio yield to worst as a raw decimal.

The result is the portfolio-level yield to worst from the aggregated metrics.

Return type:

Decimal

Parameters:

portfolio (Portfolio)

fuggers_py.portfolio.weighted_z_spread(portfolio, *, curve, settlement_date)

Return the portfolio Z-spread as a raw decimal.

Return type:

Decimal

Parameters:
  • portfolio (Portfolio)

  • curve (DiscountingCurve | None)

  • settlement_date (Date)

class fuggers_py.portfolio.WeightingMethod(value)

Weighting basis used for portfolio-level aggregation.

fuggers_py.portfolio.worst_case(results)

Return the worst-case stress result, if any.

Return type:

StressResult | None

Parameters:

results (StressSummary | Mapping[str, StressResult] | Iterable[StressResult])

class fuggers_py.portfolio.YieldComparison(portfolio_current_yield, benchmark_current_yield, active_current_yield, portfolio_ytm, benchmark_ytm, active_ytm, portfolio_ytw, benchmark_ytw, active_ytw)

Portfolio, benchmark, and active yield metrics.

Parameters:
  • portfolio_current_yield (Decimal)

  • benchmark_current_yield (Decimal)

  • active_current_yield (Decimal)

  • portfolio_ytm (Decimal)

  • benchmark_ytm (Decimal)

  • active_ytm (Decimal)

  • portfolio_ytw (Decimal)

  • benchmark_ytw (Decimal)

  • active_ytw (Decimal)

class fuggers_py.portfolio.YieldMetrics(ytm, ytw, ytc, current_yield, best_yield)

Portfolio yield metrics expressed as raw decimals.

Parameters:
  • ytm (Decimal)

  • ytw (Decimal)

  • ytc (Decimal)

  • current_yield (Decimal)

  • best_yield (Decimal)