jointview
A small marimo app for looking at two price or NAV series of a Polars DataFrame at once.
Every numeric column is a series you can pick, one on the left and one on the right. The middle holds both as two lines on one pair of axes; underneath each dropdown sits the summary of exactly the rows in that plot.
┌──────────┬───────────────────────────┬──────────┐
│ left │ two-line chart │ right │
│ dropdown │ (indexed to 100) │ dropdown │
│ ─────────│ │──────────│
│ summary │ │ summary │
│ table │ │ table │
└──────────┴───────────────────────────┴──────────┘
Both lines share one y-axis: a second scale would invent a relationship that is not in the data. So the default is to index both series to 100 at the first date they have in common, which is how a fund priced at 1.02 and one priced at 1,450 end up comparable. The switch above the plot turns that off when the levels already share a scale.
Run it
There is nothing to install: the app ships with the package, and uvx fetches both
for the length of the run.
uvx jointview # generated demo frame
uvx jointview navs.parquet # your own data
uvx jointview navs.parquet --height 900 # taller plot for a taller screen
Until the package is on PyPI, point uvx at the repository — or at a checkout of it:
uvx --from git+https://github.com/jebel-quant/jointview jointview navs.parquet
uv run jointview navs.parquet # from a clone
uv run jointview --edit # open the notebook itself
--edit only makes sense from a clone: it opens the notebook marimo is serving, and
under uvx that is a copy in a throwaway environment.
Arguments after a bare -- belong to marimo rather than to the app, which is how the
server itself is configured:
uvx jointview navs.parquet -- --port 8080 --headless
The plot takes the full width of the window and the tables sit either side of it. Its
height is the one thing the page cannot work out for itself — 700px suits a laptop, and
--height is there for a monitor that has more to give.
The file reads as .parquet, .csv, .tsv, .json, .ndjson, .arrow, .ipc and
.feather. The first temporal column becomes the x-axis; without one the rows are
numbered. Every numeric column is offered as a series. Given no file you get
jointview.data.demo_frame(): daily NAVs for six made-up funds that share a market
factor and start anywhere between 1 and 1,450.
The summary
Both tables are computed from the common sample — the dates where both series are present — so the numbers always describe the lines you are looking at.
| Observations, Start, End | the extent of the series |
| Total return, Annual return | end/start, and the same compounded to a year |
| Annual volatility, Sharpe ratio | of the period returns, at 252 periods a year, cash at zero |
| Max drawdown | the deepest fall below the running peak |
| Hit rate, Best period, Worst period | the shape of the period returns |
A figure that cannot be formed — a Sharpe ratio for a flat series, a growth rate for
a series that starts at zero — shows as — rather than blanking the table.
Use the pieces on their own
Neither the chart nor the statistics need marimo:
import polars as pl
from jointview import line_chart, summary, metrics
frame = pl.read_parquet("navs.parquet")
line_chart(frame, "tech_fund", "balanced").save("lines.html")
summary(frame["tech_fund"]) # a formatted two-column frame
metrics(frame["tech_fund"])["Sharpe ratio"] # the raw number
line_chart is a plain Altair chart: two 2px lines, a legend and a label at the end
of each line, and a crosshair that reads both series at the hovered date. Curves
longer than max_points (4,000 by default) are thinned by a fixed stride — the last
point always survives, so the endpoints and the summary agree.
Develop
uv sync
uv run pytest
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jointview-0.2.0.tar.gz.
File metadata
- Download URL: jointview-0.2.0.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8247374bf6fed56b826ec589b52a0458329509aa81e72043cc32ae46922e32d8
|
|
| MD5 |
f4e5f6458accb012ce54d7be83ffb438
|
|
| BLAKE2b-256 |
3961d1ca7ebf05ea5de73713dc7a22c7f01048b6c4bf85b37b6a57d03ca78075
|
Provenance
The following attestation bundles were made for jointview-0.2.0.tar.gz:
Publisher:
rhiza_release.yml on Jebel-Quant/jointview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jointview-0.2.0.tar.gz -
Subject digest:
8247374bf6fed56b826ec589b52a0458329509aa81e72043cc32ae46922e32d8 - Sigstore transparency entry: 2486189040
- Sigstore integration time:
-
Permalink:
Jebel-Quant/jointview@d6ea357db4106469053e704a7f7758f99f082867 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jebel-Quant
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@d6ea357db4106469053e704a7f7758f99f082867 -
Trigger Event:
push
-
Statement type:
File details
Details for the file jointview-0.2.0-py3-none-any.whl.
File metadata
- Download URL: jointview-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19113e481768e3c9d64d58abee323e4f55a20b989cbcac5adc68cf91faebd829
|
|
| MD5 |
d7fbe8ff97ca1159713f8e2d5a5e7530
|
|
| BLAKE2b-256 |
14bbd62bfb5e49cc5162335bfb4710b5ae132f22c550d267a206aab92ffb5017
|
Provenance
The following attestation bundles were made for jointview-0.2.0-py3-none-any.whl:
Publisher:
rhiza_release.yml on Jebel-Quant/jointview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jointview-0.2.0-py3-none-any.whl -
Subject digest:
19113e481768e3c9d64d58abee323e4f55a20b989cbcac5adc68cf91faebd829 - Sigstore transparency entry: 2486189258
- Sigstore integration time:
-
Permalink:
Jebel-Quant/jointview@d6ea357db4106469053e704a7f7758f99f082867 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Jebel-Quant
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
rhiza_release.yml@d6ea357db4106469053e704a7f7758f99f082867 -
Trigger Event:
push
-
Statement type: