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:
Build the dashboard objects
Numerical highlights
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']
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.