Charts¶
Charts: the .plot accessor on results, compare, and the light and dark styles. See the Gallery.
plot ¶
Charting.
Reached through a .plot accessor on the result objects (IndexResult,
BacktestResult, AttributionResult, OptimisationResult, RiskModel)
rather than through free functions taking a result. That follows the same
stance as the asset views: plotting is a result-layer concern, so a result
knows how to draw itself and nothing else grows a chart method.
result.plot.level()
backtest.plot.performance()
Needs matplotlib, which ships in the plot extra:
pip install "py-beacon-kit[plot]"
Importing beacon costs nothing extra. The accessor is a descriptor that
resolves on first access, so matplotlib is imported when a chart is drawn and
not before. import beacon works without matplotlib installed and does not
import it, and a test asserts that.
To compare several results on one chart, use beacon.plot.compare(a, b). To
restyle any matplotlib chart, use beacon.plot.use().
Every method takes ax= and returns the Axes it drew on, so charts compose
into a figure the caller laid out. The signatures carry nothing
matplotlib-specific, which is what keeps an interactive backend possible later
without changing any call site.
ChartMethods ¶
Base for the accessor classes, providing the listing repr.
A caller who types result.plot and presses enter should be told what they
can do with it. Without this they get the default object repr, which
answers a question nobody asked.
PlotAccessor ¶
Descriptor that resolves to a result's plotting methods on first use.
Attributes:
| Name | Type | Description |
|---|---|---|
accessor_name |
Class in |
accessors ¶
The chart methods reached through result.plot.
Every method takes an optional ax= and returns the Axes it drew on, so a
chart composes into a figure the caller laid out rather than insisting on its
own. That is also what keeps the signatures backend-agnostic: nothing here
returns a matplotlib-specific wrapper, so an interactive backend can offer the
same names later without the call sites changing.
Sizes come from style.FIGSIZE per kind (a level chart is wide because time
is the long axis, a weights chart tall because names stack) and only apply
when this module creates the figure. A caller passing ax= has already decided.
Where the numbers come from¶
Nowhere here. Every method reads a result object and draws it; the arithmetic belongs to the analysis layer and is tested there. The one exception is the reconciliation total annotated on the contributions chart, which the renderer recomputes from the bars it actually drew, because an annotation claiming a total that does not match the bars beside it is worse than no annotation, and the only way to be sure is to add up what is on the page.
IndexPlots ¶
Bases: ChartMethods
Charts for an IndexResult.
level ¶
The index level over time, rebased to 100.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
benchmark
|
Series | None
|
Optional comparison series, drawn subordinate. |
None
|
ax
|
Axes | None
|
Axes to draw on. A new figure is created when absent. |
None
|
label
|
str
|
Legend label for the index. |
'Index'
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
weights ¶
Constituent weights at a rebalance, with cap markers.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
date
|
Timestamp | None
|
Which rebalance. Defaults to the latest. |
None
|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
BacktestPlots ¶
Bases: ChartMethods
Charts for a BacktestResult.
performance ¶
Growth of 100 with a drawdown panel beneath it.
The two share an x axis and sit in one gridspec, because a drawdown is only meaningful against the path that produced it, and reading them side by side would mean matching dates by eye.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes | None
|
Ignored for this chart, which owns a two-panel figure. Accepted so every method has the same signature. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
The upper (level) panel. |
annual_returns ¶
Calendar-year returns as signed bars.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
AttributionPlots ¶
Bases: ChartMethods
Charts for an AttributionResult.
contributions ¶
Per-constituent contributions as diverging bars.
The drags are stated in the chart's note rather than drawn as bars, because they are comparisons against a counterfactual rather than terms in the decomposition: adding them to the same total would mix two different questions.
The annotated total is recomputed from the bars actually drawn. An annotation claiming a total that does not match what is beside it is worse than none, and adding up the page is the only way to be sure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
OptimisationPlots ¶
Bases: ChartMethods
Charts for an OptimisationResult.
exposures ¶
Active weights as sign-coloured tilts against the index.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
frontier ¶
The efficient frontier, with the named points and the capital line.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
frontier
|
Any
|
An |
required |
risk_free_rate
|
float
|
Where the capital market line starts. |
0.0
|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
RiskPlots ¶
Bases: ChartMethods
Charts for a RiskModel.
correlation ¶
The correlation matrix as a heatmap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes | None
|
Axes to draw on. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
base ¶
The lazy .plot accessor.
Every result object carries one, and none of them cost anything until it is
touched. That is the whole point of this module: it imports nothing beyond the
standard library, so import beacon does not import matplotlib, and a user
who never draws a chart never pays for the ability to.
The mechanism is a descriptor. IndexResult.plot is an attribute lookup that
resolves, on first access, to a class in beacon.plot.accessors, which is
where matplotlib is required. Accessed on the class rather than an instance,
it returns the descriptor itself, so help() and hasattr work without
matplotlib.
PlotAccessor ¶
Descriptor that resolves to a result's plotting methods on first use.
Attributes:
| Name | Type | Description |
|---|---|---|
accessor_name |
Class in |
ChartMethods ¶
Base for the accessor classes, providing the listing repr.
A caller who types result.plot and presses enter should be told what they
can do with it. Without this they get the default object repr, which
answers a question nobody asked.
comparison ¶
Comparing several results on one axis.
A free function rather than an accessor because it is not about any one result:
compare(a, b, c) is a statement about the set, and hanging it off the first
argument would make that arbitrary. Import it as
from beacon.plot import compare.
Aligned, not concatenated¶
Every series is clipped to the window they all share and rebased to 100 on the first shared date. Two indices with different histories compared over different periods differ for no reason but their spans, and the one with the shorter history looks better or worse than it is. Rebasing on the shared start means the lines begin together and the comparison is of shape.
compare ¶
Plot several results on one rebased axis, with a metrics table.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*results
|
Any
|
Two or more |
()
|
labels
|
list[str] | None
|
Display names. Defaults to each result's own identifier (its index id or portfolio id), or "Series N" when it has none. |
None
|
ax
|
Axes | None
|
Axes to draw on. A new figure is created when absent. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Axes |
Axes
|
What was drawn on. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If fewer than two results are given, or they share no dates. |
style ¶
The "beacon" matplotlib styles, generated from the design tokens.
Not hand-written: every colour comes from beacon.tokens, which is a vendored
copy of the file the desktop client generates its CSS from. That is the point:
a chart embedded in the application should be the same colours as the panel
around it, and the only way to guarantee that is for both to read one source.
A palette retyped here would be right on the day it was written.
Two styles, beacon and beacon-dark, because the client has two modes and a
chart has to follow. Both are registered with matplotlib by name, so once
:func:register has run (every chart method, :func:use and
beacon.plot.compare run it) plt.style.use("beacon") restyles any plot,
including one this library never drew.
What the grammar is¶
- an accent line at 1.5pt for the primary series, text-secondary for a benchmark: the comparison should read as subordinate without being hidden
- divider gridlines on the y axis only, and behind the data
- muted axis labels and tick text, with the top and right spines removed: a chart is mostly data, and the frame is not the data
The correlation colormap¶
beacon_corr is registered here too, from the raw.heatmap-* tokens.
Mode-independent by design: a correlation of 0.8 must look the same whichever
theme the surrounding application is wearing, or two screenshots of the same
matrix disagree.
palette ¶
The colours a chart draws with, in one mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode
|
str
|
LIGHT or DARK. |
LIGHT
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, str]
|
The token names this module uses, so a caller composing a custom |
dict[str, str]
|
chart can reach the same values rather than sampling them off a figure. |
style_dict ¶
The rcParams for one mode.
Built as a mapping rather than written to an .mplstyle file so the values
stay derived from the tokens. A generated file would be a second copy to
keep in step, which is the thing this module exists to avoid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode
|
str
|
LIGHT or DARK. |
LIGHT
|
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Any]
|
matplotlib rcParams names to values. |
correlation_colormap ¶
The beacon_corr colormap, green through amber to red.
Built from the mode-independent raw.heatmap-* tokens: a correlation of
0.8 must look the same whichever theme the application is wearing, or two
screenshots of one matrix disagree.
Returns:
| Name | Type | Description |
|---|---|---|
Colormap |
Any
|
Registered under CORRELATION_COLORMAP. |
register ¶
Register both styles and the colormap with matplotlib.
Idempotent, and called on first use of any accessor, so
plt.style.use("beacon") works as soon as anything in this package has
been touched. Registering at import of beacon.plot instead would mean
importing matplotlib to do it, which is exactly what the lazy accessor
exists to avoid.
use ¶
Apply a beacon style globally.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mode
|
str
|
LIGHT or DARK. |
LIGHT
|