Ctrl K
ComunidadGuías…

app/chart_templates.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""The default charts, written in the builder's own vocabulary.

Every chart the app ships is a spec here — nothing is drawn by hand-written
code any more. That is the point: if a default can't be expressed as a spec,
then the builder can't reproduce it, and somebody who edits or deletes it can
never get it back.

A new account is seeded with these. From that moment they are that person's
charts: editing one changes their copy, deleting one removes it, and the
template stays here to be re-added from the builder's "Start from a template"
list.
"""

from __future__ import annotations

# key → template. `key` is stable and recorded on the saved chart, so a chart
# that came from a template can still be told apart from one built by hand.
TEMPLATES: tuple[dict, ...] = (
    {
        "key": "today",
        "name": "Percentage difference today",
        "blurb": "Last close vs previous close, per open holding",
        "width": "full",
        "spec": {
            "grain": "positions",
            "x": "ticker",
            "measures": ["pos_day_pct"],
            "type": "bar",
        },
    },
    {
        "key": "portfolio",
        "name": "Portfolio over time",
        "blurb": "Invested, market value and net gain, daily",
        "width": "full",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["value", "invested", "gain"],
            "type": "line",
            "bucket": "day",
        },
    },
    {
        "key": "periods",
        "name": "Performance summary",
        "blurb": "1 day to 20 years: what you put in, what it made, the return",
        "width": "full",
        "spec": {
            "grain": "periods",
            "x": "period",
            "measures": ["p_from", "p_value_then", "p_contributed", "p_growth",
                         "p_on_money_in", "p_twr", "p_twr_annual"],
            "type": "table",
        },
    },
    {
        "key": "alloc_invested",
        "name": "Total invested %",
        "blurb": "All-time outlay by asset class",
        "width": "half",
        "spec": {
            "grain": "positions",
            "x": "asset_class",
            "measures": ["pos_cost"],
            "type": "doughnut",
        },
    },
    {
        "key": "alloc_value",
        "name": "Total position %",
        "blurb": "Current value by asset class",
        "width": "half",
        "spec": {
            "grain": "positions",
            "x": "asset_class",
            "measures": ["pos_value"],
            "type": "doughnut",
        },
    },
    {
        "key": "yrpct",
        "name": "Yearly performance",
        "blurb": "Gain in each financial year as % of what was invested",
        "width": "half",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["period_return"],
            "type": "bar",
            "bucket": "fy",
        },
    },
    {
        "key": "gainpct",
        "name": "Overall performance by year",
        "blurb": "Cumulative return at each financial year end",
        "width": "half",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["gain_pct"],
            "type": "bar",
            "bucket": "fy",
        },
    },
    {
        "key": "cum",
        "name": "Year-end growth",
        "blurb": "Cumulative invested vs cumulative value gain",
        "width": "half",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["invested", "value_gain"],
            "type": "bar",
            "bucket": "fy",
        },
    },
    {
        "key": "yearly",
        "name": "Yearly invested & return",
        "blurb": "What went in and what it made, per financial year",
        "width": "half",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["invested_delta", "gain_delta"],
            "type": "bar",
            "bucket": "fy",
        },
    },
    {
        "key": "month_ends",
        "name": "Month ends",
        "blurb": "The daily series as a table, one row per month",
        "width": "full",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["invested", "value", "gain", "gain_pct"],
            "type": "table",
            "bucket": "month",
        },
    },
    # Beyond the originals — cheap to offer once everything is a spec, and the
    # obvious next questions someone asks of a portfolio.
    {
        "key": "value_by_ticker",
        "name": "Value by holding",
        "blurb": "What each position is worth right now",
        "width": "half",
        "spec": {
            "grain": "positions",
            "x": "ticker",
            "measures": ["pos_value"],
            "type": "hbar",
        },
    },
    {
        "key": "gain_by_ticker",
        "name": "Unrealised gain % by holding",
        "blurb": "How each position's units held have done against their cost",
        "width": "half",
        "spec": {
            "grain": "positions",
            "x": "ticker",
            "measures": ["pos_gain_pct"],
            "type": "hbar",
        },
    },
    {
        "key": "value_over_time_by_holding",
        "name": "Value over time, by holding",
        "blurb": "One line per holding — how each has grown, not just the total",
        "width": "full",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["value"],
            "split": "ticker",
            "type": "line",
            "bucket": "month",
        },
    },
    {
        "key": "value_over_time_by_class",
        "name": "Value over time, by asset class",
        "blurb": "How the mix between ETFs, shares and crypto has shifted",
        "width": "full",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["value"],
            "split": "asset_class",
            "type": "area",
            "bucket": "month",
        },
    },
    {
        "key": "returns_by_period",
        "name": "Return by period",
        "blurb": "The time-weighted return over each window, as bars",
        "width": "half",
        "spec": {
            "grain": "periods",
            "x": "period",
            "measures": ["p_twr"],
            "type": "bar",
        },
    },
    {
        "key": "value_by_ticker_currency",
        "name": "Value by holding and currency",
        "blurb": "Each position stacked by the currency it trades in",
        "width": "half",
        "spec": {
            "grain": "positions",
            "x": "ticker",
            "measures": ["pos_value"],
            "split": "currency",
            "type": "bar",
        },
    },
    {
        "key": "income",
        "name": "Cash income by year",
        "blurb": "Distributions taken as cash rather than reinvested",
        "width": "half",
        "spec": {
            "grain": "timeseries",
            "x": "date",
            "measures": ["cash_div"],
            "type": "bar",
            "bucket": "fy",
        },
    },
)

BY_KEY = {t["key"]: t for t in TEMPLATES}

# What a new account starts with, in this order. The extras above are offered
# in the builder but not applied unasked — a first look at the page should be
# the set we designed, not everything we could think of.
DEFAULT_KEYS = (
    "today",
    "portfolio",
    "periods",
    "alloc_invested",
    "alloc_value",
    "yrpct",
    "gainpct",
    "cum",
    "yearly",
    "month_ends",
)