Stock Portfolio

Build a wider market-data table with grouped headers, editable tickers, sparklines, and currency columns.

Stock Portfolio

Build a wider market-data table with grouped headers, editable tickers, sparklines, and currency columns.

Source: usage.py::build_stock_portfolio_code().

def load_stock_portfolio_record(symbol, row_id):
    ticker = yf.Ticker(symbol)
    history = ticker.history(period="3mo")["Close"].dropna().tolist()
    return {
        "id": row_id,
        "ticker": ticker.info["displayName"],
        "_queryTicker": symbol,
        "Exchange": ticker.fast_info["exchange"],
        "sector": ticker.info["sector"],
        "lastValue": ticker.analyst_price_targets["current"],
        "marketCap": ticker.fast_info["marketCap"],
        "priceTargetLow": ticker.analyst_price_targets["low"],
        "priceTargetMedian": ticker.analyst_price_targets["median"],
        "priceTargetHigh": ticker.analyst_price_targets["high"],
        "sparkline": history,
        "sparklineLabel": history,
        "sparklineColor": "blue",
    }

stock_records = [
    load_stock_portfolio_record(symbol, row_id)
    for row_id, symbol in enumerate(STOCK_PORTFOLIO_DEFAULT_TICKERS, start=1)
]

portfolio_table = (
    dmdt.DataTable(
        id="stock-portfolio-table",
        data=stock_records,
        columns=STOCK_PORTFOLIO_COLUMNS,
        idAccessor="id",
        paginationMode="none",
    )
    .update_layout(
        radius="lg",
        withTableBorder=True,
        withColumnBorders=True,
        striped=True,
        pinFirstColumn=True,
        textSelectionDisabled=True,
    )
    .update_columns(
        selector="ticker",
        title="Stock ticker",
        width=140,
        editable=True,
        editor=dmc.TextInput(label="Stock ticker", placeholder="e.g. NVDA"),
        cellsStyle={"fontWeight": 700, "letterSpacing": "0.04em", "textTransform": "uppercase"},
    )
    .update_columns(selector="Exchange", title="Exchange", width=120)
    .update_columns(selector="sector", title="Sector", width=190)
    .update_columns(
        selector="lastValue",
        title="Last value",
        presentation="currency",
        currency="USD",
        textAlign="right",
        width=130,
        cellsStyle={"fontVariantNumeric": "tabular-nums"},
    )
    .update_columns(
        selector="marketCap",
        title="Market cap",
        presentation="currency",
        currency="USD",
        textAlign="right",
        width=150,
        cellsStyle={"fontVariantNumeric": "tabular-nums"},
    )
    .update_columns(
        selector=["priceTargetLow", "priceTargetMedian", "priceTargetHigh"],
        presentation="currency",
        currency="USD",
        textAlign="right",
        width=120,
        cellsStyle={"fontVariantNumeric": "tabular-nums"},
    )
    .update_columns(selector="priceTargetLow", title="Low")
    .update_columns(selector="priceTargetMedian", title="Median", cellsStyle={"fontWeight": 700})
    .update_columns(selector="priceTargetHigh", title="High")
    .update_columns(
        selector="sparkline",
        title="3M history",
        width=160,
        render=dmc.Sparkline(data="{sparkline}", color="{sparklineColor}", w=140, h=34),
    )
    .group_columns(
        dmdt.ColumnGroup(
            "snapshot",
            title="Snapshot",
            columns=["ticker", "Exchange", "sector", "lastValue", "marketCap"],
        ),
        dmdt.ColumnGroup(
            "price-target",
            title="Price target",
            textAlign="center",
            columns=["priceTargetLow", "priceTargetMedian", "priceTargetHigh"],
        ),
        dmdt.ColumnGroup(
            "history",
            title="History",
            columns=["sparkline"],
        )
    )
    .group_columns(
        selector="snapshot",
        style={"backgroundColor": "var(--mantine-color-gray-0)"},
    )
    .group_columns(
        selector="price-target",
        style={"backgroundColor": "var(--mantine-color-blue-0)"},
    )
    .group_columns(
        selector="history",
        style={"backgroundColor": "var(--mantine-color-green-0)"},
    )
)

# In the docs app, a callback reloads the live rows on page load and
# re-hydrates the table after ticker edits.