Report charts: re-basing-on-zoom widget (same as Equity tab); layout fixes

- per-fund report charts now use chart_widget.equity_chart_html
  (st.iframe) instead of static st.plotly_chart: every visible window
  re-bases each line to 1.0 at its left edge, so fund/reference/index
  compare in any zoom level - same semantics as the Equity curves tab
- removed 'expand all' checkbox (24 expanded sections drowned the page)
- added 'rest of Fund Lab continues below' marker after the report
  section; the other Fund Lab sections (alpha search, clusters, N-PORT,
  drawdown, tax, CEF) were never deleted and are unchanged
This commit is contained in:
Greg Pomerantz 2026-08-30 18:15:11 -04:00
parent 3fbf332b31
commit e28f8fcf85
2 changed files with 43 additions and 25 deletions

55
app.py
View File

@ -639,8 +639,6 @@ with tab_fundlab:
})
st.dataframe(pd.DataFrame(_ag), width="stretch",
hide_index=True)
_rall = st.checkbox("Expand all fund reports", value=False,
key="fl_rpt_expand")
for _s in _rorder:
_f = _rfunds[_s]
_m, _st = _f["meta"], _f.get("stats", {})
@ -649,35 +647,38 @@ with tab_fundlab:
f"{_m['name']}")
if _m.get("verdict"):
_title += f" [{_m['verdict'][:44]}]"
with st.expander(_title, expanded=_rall):
with st.expander(_title):
if _f.get("narrative"):
st.markdown("\n\n".join(_f["narrative"]))
# same re-basing-on-zoom widget as the Equity curves tab:
# every visible window re-bases each line to 1.0 at its
# left edge, so fund/reference/index are comparable no
# matter where you zoom
_c = _f.get("chart", {})
if _c.get("dates"):
import plotly.graph_objects as _go
_fig = _go.Figure()
_fig.add_trace(_go.Scatter(
x=pd.to_datetime(_c["dates"]),
y=_c["fund"], name=_m["sym"].upper(),
line=dict(width=2)))
from chart_widget import equity_chart_html
_ds = pd.to_datetime(_c["dates"])
_series = {_m["sym"].upper():
pd.Series(_c["fund"], _ds)}
if _c.get("ref"):
_fig.add_trace(_go.Scatter(
x=pd.to_datetime(_c["ref_dates"]),
y=_c["ref"], name="fitted reference",
line=dict(width=1.2, dash="dash")))
_series["fitted reference"] = pd.Series(
_c["ref"], pd.to_datetime(_c["ref_dates"]))
if _c.get("ivv"):
_fig.add_trace(_go.Scatter(
x=pd.to_datetime(_c["ivv_dates"]),
y=_c["ivv"], name="S&P 500 (IVV)",
line=dict(width=1, dash="dot"),
opacity=0.7))
_fig.update_layout(
height=420, margin=dict(l=10, r=10, t=25, b=10),
legend=dict(orientation="h", y=1.1),
hovermode="x unified",
title=f"total return since "
f"{_c['dates'][0][:4]} (rebased 100)")
st.plotly_chart(_fig, width="stretch")
_series["S&P 500 (IVV)"] = pd.Series(
_c["ivv"], pd.to_datetime(_c["ivv_dates"]))
st.iframe(equity_chart_html(
{k: s / s.iloc[0]
for k, s in _series.items()},
_ds, height=460, ytitle="growth (1.0 = start)",
styles={
_m["sym"].upper(): {"width": 2},
"fitted reference":
{"width": 1, "dash": "dash",
"opacity": 0.8},
"S&P 500 (IVV)":
{"width": 1, "dash": "dot",
"opacity": 0.5},
}), height=490)
if _f.get("perf"):
st.dataframe(pd.DataFrame(
_f["perf"],
@ -713,6 +714,10 @@ with tab_fundlab:
"R² 5y", "alpha 5y", "tax"]),
width="stretch", hide_index=True)
st.caption(_pe["proscons"])
st.caption("← the rest of the Fund Lab continues below: "
"alpha search, return-driver clusters, N-PORT "
"cross-check, drawdown resilience, tax location, "
"and the CEF ranking.")
# --- alpha search: all screened funds (shortlist + longlist + harvest)
with st.expander("Alpha search — all screened funds, ranked"):

View File

@ -1136,3 +1136,16 @@ chart, period table, drivers, reference mix, tax, peers) + an "expand
all" checkbox. Static build so the page stays fast; regenerate with
`python -m fundlab.reportdata` (~8 s) and `python -m fundlab.report`
(~11 s for the HTML).
**Report charts: re-basing-on-zoom widget (2nd pass).** The first report
build used static st.plotly_chart with pre-rebased series — zooming to a
window left the lines starting at different levels (no comparison
possible). Replaced with the SAME widget as the Equity curves tab
(`chart_widget.equity_chart_html` + st.iframe): on every relayout each
visible line is re-based to 1.0 at its left edge and y fits the visible
data, so fund/reference/index are comparable in any window. report_data
still stores the full-history series; the app divides by each series'
first point before handing it to the widget (per-series 1.0 start, as
the widget expects). Also removed the "expand all" checkbox (24 expanded
sections made the page unusable and hid the sections below) and added a
"rest of the Fund Lab continues below" marker.