Exportable Reports¶
AbaQuant’s reporting layer (abaquant.reports) turns option models,
portfolio allocators, backtests, credit-proxy assessments, and integrated
risk dashboards into Markdown, HTML, or PDF deliverables — using only
the standard library plus pandas (already a dependency). The PDF exporter
is a lightweight, pure-Python writer; it doesn’t need reportlab,
weasyprint, wkhtmltopdf, LaTeX, or a browser runtime.
Sections:
Build the underlying objects (option model, credit assessment, allocator, backtest, dashboard)
Export reports in each format
Setup¶
import abaquant
print(f"AbaQuant version: {abaquant.__version__}")
AbaQuant version: 1.0.0rc1
from pathlib import Path
import numpy as np
import pandas as pd
from abaquant import RiskDashboard
from abaquant.credit import (
BalanceSheetInputs,
CashFlowInputs,
CreditAnalysisInputs,
CreditHistoricalSeries,
IncomeStatementInputs,
MarketEquityObservation,
PriorPeriodInputs,
calculate_credit_proxy_metrics,
)
from abaquant.derivatives.models import BlackScholesMertonModel
from abaquant.portfolio import PortfolioAllocator
REPORT_DIR = Path("generated_reports")
REPORT_DIR.mkdir(parents=True, exist_ok=True)
def sample_returns() -> pd.DataFrame:
dates = pd.date_range("2025-01-02", periods=36, freq="B")
trend = np.linspace(0.0, 1.0, len(dates))
seasonal = np.sin(np.linspace(0.0, 4.0 * np.pi, len(dates)))
prices = pd.DataFrame(
{
"ALPHA": 100.0 + 9.0 * trend + 1.8 * seasonal,
"BETA": 82.0 + 6.0 * trend - 1.2 * seasonal,
"GAMMA": 54.0 + 3.0 * trend + 0.7 * np.cos(np.linspace(0.0, 3.0 * np.pi, len(dates))),
},
index=dates,
)
return prices.pct_change().dropna()
1. Build the underlying objects¶
option_model = BlackScholesMertonModel(
spot_price=100.0, strike_price=105.0, maturity_years=1.0,
risk_free_rate=0.04, volatility=0.22, dividend_yield=0.01,
)
credit_assessment = calculate_credit_proxy_metrics(
CreditAnalysisInputs(
balance_sheet=BalanceSheetInputs(
total_debt=120.0, total_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,
),
income_statement=IncomeStatementInputs(
revenue=450.0, gross_profit=200.0, ebit=75.0, ebitda=90.0,
interest_expense=10.0, net_income=60.0,
),
cash_flow_statement=CashFlowInputs(operating_cash_flow=70.0),
prior_period=PriorPeriodInputs(
total_assets=470.0, net_income=55.0, long_term_debt=90.0, current_assets=220.0,
current_liabilities=105.0, shares_outstanding=100.0, gross_profit=180.0, revenue=420.0,
),
market_equity=MarketEquityObservation(market_value_equity=650.0),
historical_series=CreditHistoricalSeries(
earnings_history=(42.0, 47.0, 53.0, 60.0),
leverage_history=(0.60, 0.54, 0.48, 0.42),
),
reporting_currency="USD",
reporting_period="FY2025",
)
)
allocator = PortfolioAllocator(sample_returns(), annual_risk_free_rate=0.02)
backtest = allocator.backtest(
weights="inverse_volatility",
rebalance="monthly",
transaction_cost_bps=5.0,
slippage_bps=1.0,
benchmark="equal_weight",
lookback=10,
)
dashboard = RiskDashboard(
allocator,
credit_assessments={"ALPHA": credit_assessment},
weights=backtest.weights_history.iloc[-1],
backtest=backtest,
)
2. Export reports in each format¶
Every high-level object exposes .report(), which returns an
ExportableReport. Call .save(directory, stem, formats=...) to write one
or more formats in a single call.
reports = {
"option_markdown": option_model.report(option_type="call").save(
REPORT_DIR, "option_report", formats=("markdown",)
)["markdown"],
"option_html": option_model.report(option_type="call").save(
REPORT_DIR, "option_report", formats=("html",)
)["html"],
"option_pdf": option_model.report(option_type="call").save(
REPORT_DIR, "option_report", formats=("pdf",)
)["pdf"],
"portfolio_html": allocator.report().save(
REPORT_DIR, "portfolio_report", formats=("html",)
)["html"],
"backtest_markdown": backtest.report().save(
REPORT_DIR, "backtest_report", formats=("markdown",)
)["markdown"],
"credit_markdown": credit_assessment.report().save(
REPORT_DIR, "credit_proxy_report", formats=("markdown",)
)["markdown"],
"risk_dashboard_html": dashboard.report().save(
REPORT_DIR, "risk_dashboard_report", formats=("html",)
)["html"],
}
for name, path in reports.items():
print(f"{name:24s}: {path}")
option_markdown : generated_reports/option_report.md
option_html : generated_reports/option_report.html
option_pdf : generated_reports/option_report.pdf
portfolio_html : generated_reports/portfolio_report.html
backtest_markdown : generated_reports/backtest_report.md
credit_markdown : generated_reports/credit_proxy_report.md
risk_dashboard_html : generated_reports/risk_dashboard_report.html
Takeaway¶
Reports are ReportSection/ReportTable objects under the hood, so you
can also build custom sections directly from abaquant.reports if the
built-in .report() shape doesn’t match your needs. Numerical result
objects remain the source of truth — reports are a downstream presentation
layer.