Minimal Credit Proxy Examples

Two small, focused examples of the fundamentals-based credit-proxy layer:

  1. Manual inputs — the absolute minimum accounting inputs needed for a handful of proxy metrics, entered by hand.

  2. Cached, provider-fed inputs — the same pipeline, but starting from cached financial-statement snapshots retrieved through a MarketTicker.

These are good starting points before the fuller workflow in notebook 04 — Credit Risk.

Setup

import abaquant
print(f"AbaQuant version: {abaquant.__version__}")
AbaQuant version: 1.0.0rc1

1. Manual credit-proxy inputs

Declare only the balance-sheet, income-statement, and cash-flow fields you actually have, and let calculate_credit_proxy_metrics compute whatever metrics those inputs support.

from abaquant.credit.fundamentals import (
    BalanceSheetInputs,
    CashFlowInputs,
    CreditAnalysisInputs,
    IncomeStatementInputs,
    calculate_credit_proxy_metrics,
)

manual_inputs = CreditAnalysisInputs(
    balance_sheet=BalanceSheetInputs(
        total_debt=120.0,
        total_equity=300.0,
        current_assets=250.0,
        current_liabilities=100.0,
        cash_and_cash_equivalents=50.0,
    ),
    income_statement=IncomeStatementInputs(
        ebit=75.0,
        ebitda=90.0,
        interest_expense=10.0,
    ),
    cash_flow_statement=CashFlowInputs(operating_cash_flow=70.0),
    reporting_currency="USD",
    reporting_period="FY2025",
)

manual_assessment = calculate_credit_proxy_metrics(manual_inputs)
manual_summary = {
    "debt_to_equity": manual_assessment.metrics["debt_to_equity"],
    "current_ratio": manual_assessment.metrics["current_ratio"],
    "interest_coverage": manual_assessment.metrics["interest_coverage"],
    "synthetic_score": manual_assessment.synthetic_credit_proxy_score,
    "synthetic_band": manual_assessment.synthetic_credit_proxy_band,
}
for key, value in manual_summary.items():
    print(f"{key:20s}: {value}")
debt_to_equity      : 0.4
current_ratio       : 2.5
interest_coverage   : 7.5
synthetic_score     : 94.92
synthetic_band      : strong_balance_sheet_proxy

2. Cached, provider-fed credit-proxy inputs

Instead of typing numbers by hand, retrieve them from a MarketTicker backed by an offline (or, in production, live) provider. The statement snapshot is cached in memory so repeated accessor calls (total_debt(), ebitda(), …) reuse one retrieval.

import pandas as pd

from abaquant.marketdata import get_ticker


class DeterministicMarketDataProvider:
    """Minimal offline provider supplying one cached financial snapshot."""

    name = "deterministic-example"

    def fast_info(self, symbol):
        return {"lastPrice": 105.0}

    def info(self, symbol):
        return {"currency": "USD", "marketCap": 600.0, "symbol": symbol}

    def history(self, symbol, **kwargs):
        dates = pd.date_range("2025-01-02", periods=12, freq="B")
        return pd.DataFrame({"Close": [100.0 + i for i in range(len(dates))]}, index=dates)

    def history_many(self, symbols, **kwargs):
        return self.history(None).reindex(columns=list(symbols), method="nearest")

    def option_expirations(self, symbol):
        return []

    def option_chain(self, symbol, expiry):
        empty = pd.DataFrame(columns=["strike", "lastPrice", "impliedVolatility"])
        return empty, empty

    def income_statement(self, symbol, *, period="annual"):
        return pd.DataFrame({"2025-12-31": {
            "Total Revenue": 450.0, "EBITDA": 90.0, "EBIT": 75.0,
            "Interest Expense": 10.0, "Net Income": 60.0, "Gross Profit": 200.0,
        }})

    def balance_sheet(self, symbol, *, period="annual"):
        return pd.DataFrame({"2025-12-31": {
            "Total Debt": 120.0, "Stockholders Equity": 300.0, "Current Assets": 250.0,
            "Inventory": 40.0, "Current Liabilities": 100.0, "Cash And Cash Equivalents": 50.0,
            "Total Assets": 500.0, "Total Liabilities": 200.0, "Retained Earnings": 110.0,
            "Long Term Debt": 80.0,
        }})

    def cash_flow_statement(self, symbol, *, period="annual"):
        return pd.DataFrame({"2025-12-31": {"Operating Cash Flow": 70.0}})


cached_ticker = get_ticker(
    "DEMO", provider=DeterministicMarketDataProvider(), financial_cache="memory"
)
snapshot = cached_ticker.financials.snapshot(period="annual")
cached_assessment = cached_ticker.credit.assess_from_financials(period="annual")

cached_summary = {
    "statement_provider": snapshot.provider_name,
    "total_debt": cached_ticker.financials.total_debt(),
    "ebitda": cached_ticker.financials.ebitda(),
    "operating_cash_flow": cached_ticker.financials.operating_cash_flow(),
    "credit_proxy_score": cached_assessment.synthetic_credit_proxy_score,
}
for key, value in cached_summary.items():
    print(f"{key:20s}: {value}")
statement_provider  : deterministic-example
total_debt          : 120.0
ebitda              : 90.0
operating_cash_flow : 70.0
credit_proxy_score  : 96.34

Takeaway

Both paths converge on the same CreditAnalysisInputs / calculate_credit_proxy_metrics pipeline — pick manual inputs for quick, one-off calculations, and provider-fed inputs when you want the numbers sourced (and cached) from a real statement pipeline.