Portfolio–Credit Visual Dashboard

A combined dashboard that pairs a portfolio allocation with a ticker’s credit-proxy assessment and a multi-ticker universe view — the kind of one-page review you might build for a research note.

Sections:

  1. Build the dashboard objects

  2. Numerical highlights

  3. The dashboard figure set

Setup

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

from abaquant.marketdata import get_ticker, get_tickers
from abaquant.portfolio.optimization import PortfolioAllocator
from abaquant.visualization import VisualizationError
class DeterministicMarketDataProvider:
    """Minimal offline provider for this dashboard example."""

    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):
        import numpy as np
        dates = pd.date_range("2025-01-02", periods=24, freq="B")
        return pd.DataFrame({"Close": 100.0 + np.linspace(0, 8, len(dates))}, index=dates)

    def history_many(self, symbols, **kwargs):
        import numpy as np
        dates = pd.date_range("2025-01-02", periods=24, freq="B")
        data = {s: 100.0 + i * 5 + np.linspace(0, 8, len(dates)) for i, s in enumerate(symbols)}
        return pd.DataFrame(data, index=dates)

    def option_expirations(self, symbol):
        return ["2027-01-15"]

    def option_chain(self, symbol, expiry):
        strikes = [80.0, 100.0, 120.0]
        calls = pd.DataFrame({
            "contractSymbol": [f"{symbol}C{int(k)}" for k in strikes], "strike": strikes,
            "lastPrice": [22.0, 8.0, 2.4], "bid": [21.5, 7.6, 2.0], "ask": [22.5, 8.4, 2.8],
            "impliedVolatility": [0.31, 0.23, 0.29], "openInterest": [120, 520, 180],
            "volume": [12, 65, 16],
        })
        puts = pd.DataFrame({
            "contractSymbol": [f"{symbol}P{int(k)}" for k in strikes], "strike": strikes,
            "lastPrice": [2.2, 7.8, 21.0], "bid": [1.9, 7.4, 20.4], "ask": [2.5, 8.2, 21.6],
            "impliedVolatility": [0.35, 0.24, 0.32], "openInterest": [210, 610, 155],
            "volume": [18, 70, 14],
        })
        return calls, puts

    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}})

1. Build the dashboard objects

A portfolio allocator, one ticker (for credit), and one universe.

returns = pd.DataFrame(
    {
        "ALPHA": [0.01, -0.002, 0.006, 0.004, 0.003],
        "BETA": [0.003, 0.005, -0.001, 0.002, 0.004],
        "GAMMA": [-0.002, 0.007, 0.004, 0.006, 0.001],
    }
)
allocator = PortfolioAllocator(returns, annual_risk_free_rate=0.02)

provider = DeterministicMarketDataProvider()
ticker = get_ticker("DEMO", provider=provider, financial_cache="memory")
universe = get_tickers(["ALPHA", "BETA", "GAMMA"], provider=provider)

2. Numerical highlights

allocation = allocator.mean_variance.maximum_sharpe()
credit_assessment = ticker.credit.assess_from_financials()
universe_portfolio = universe.portfolio.max_sharpe(period="1mo", risk_free_rate=0.02)

metrics = {
    "portfolio_alpha_weight": float(allocation[0]),
    "credit_proxy_score": credit_assessment.synthetic_credit_proxy_score,
    "credit_proxy_band": credit_assessment.synthetic_credit_proxy_band,
    "universe_sharpe_ratio": universe_portfolio.sharpe_ratio,
}
for key, value in metrics.items():
    print(f"{key:26s}: {value}")
portfolio_alpha_weight    : 0.32184437791129206
credit_proxy_score        : 96.34
credit_proxy_band         : strong_balance_sheet_proxy
universe_sharpe_ratio     : 745.6999002212838

3. The dashboard figure set

Allocation weights, cumulative return path, asset correlation, ticker/universe price history, one statement, and credit metrics/score.

try:
    figures = {
        "allocation_weights": allocator.visualize(weights=allocation, chart="weights"),
        "portfolio_path": allocator.visualize(weights=allocation, chart="cumulative_returns"),
        "asset_correlation": allocator.visualize(chart="correlation"),
        "ticker_history": ticker.visualize(period="1mo"),
        "universe_history": universe.visualize(period="1mo"),
        "financial_statement": ticker.financials.visualize(statement="balance_sheet"),
        "credit_metrics": credit_assessment.visualize(chart="metrics"),
        "credit_score": credit_assessment.visualize(chart="score"),
    }
    print(f"Created {len(figures)} dashboard figures: {list(figures)}")
except VisualizationError as exc:
    print(f"Visualization skipped (optional dependency missing): {exc}")
Created 8 dashboard figures: ['allocation_weights', 'portfolio_path', 'asset_correlation', 'ticker_history', 'universe_history', 'financial_statement', 'credit_metrics', 'credit_score']
../../_images/47f752cfcd6a8cfefd9a75d722dfdb59ca9ee452fc4a37a6c804c50f9257cf66.png ../../_images/de16cd701c0a2ccbe5a7c8db2db31b266a28b25aa3efe7a679e7e65ec5c23a45.png ../../_images/441a5cb26c56e4cfe47d738322a686e6e6572c6f3949e25763d940ff23da0e96.png ../../_images/f31e40afafccff591d049f5ebc3dbafa31a0973ab58f09fcb6a5b259589db598.png ../../_images/1744989966747151eb700eba574dbc05c08a34bf5d893850a3c671ee55f766a2.png ../../_images/8537402376942403f1b1d6c4ea5737d678eb22db9feaeeee24e4b78c3c741caa.png ../../_images/8d01831176fd87d2909ed9a7e92b5c787715462348dff102c5bc3dfa980fda3e.png ../../_images/c4d22ddf73b111545334c8c3ce77b850b513319361b874328d35da46fbb17fc3.png

Takeaway

This pattern — one facade per domain, each contributing a few figures to a shared dashboard — scales well. See notebook 20 — Risk Dashboard for AbaQuant’s built-in RiskDashboard object, which formalizes this pattern with .visualize() and .report() support out of the box.