CEF app tab: full 239-fund verified tax-arb ranking
This commit is contained in:
parent
857dfce19f
commit
6f6d6b8047
57
app.py
57
app.py
|
|
@ -1110,51 +1110,36 @@ with tab_fundlab:
|
||||||
"table is a rough keyword classification. N-PORT holdings are "
|
"table is a rough keyword classification. N-PORT holdings are "
|
||||||
"quarterly and up to ~60 days stale.")
|
"quarterly and up to ~60 days stale.")
|
||||||
|
|
||||||
# --- closed-end funds (tax-arb shortlist) ---------------------------
|
# --- closed-end funds (tax-arb, actual distribution character) ------
|
||||||
import pandas as _pd
|
import pandas as _pd
|
||||||
_fl_dir = Path(__file__).parent / "fundlab"
|
_fl_dir = Path(__file__).parent / "fundlab"
|
||||||
_char_path = _fl_dir / "cef_character.json"
|
_rank_path = _fl_dir / "cef_rank_all.json"
|
||||||
if _char_path.exists():
|
if _rank_path.exists():
|
||||||
_char = json.loads(_char_path.read_text())
|
_rank = json.loads(_rank_path.read_text())
|
||||||
_annual = {}
|
_univ = json.loads((_fl_dir / "cef_universe.json").read_text())
|
||||||
_ann_path = _fl_dir / "cef_annual.json"
|
|
||||||
if _ann_path.exists():
|
|
||||||
_annual = json.loads(_ann_path.read_text())
|
|
||||||
_rows = []
|
_rows = []
|
||||||
for _s, _c in _char.items():
|
for _score, _s, _ch, _t5, _vol, _npos, _tend, _disc in _rank:
|
||||||
_a = _annual.get(_s.upper(), {})
|
|
||||||
_ch = _c.get("character")
|
|
||||||
_actual = False
|
|
||||||
if _a.get("char_actual") is not None:
|
|
||||||
_ch = _a["char_actual"]
|
|
||||||
_actual = True
|
|
||||||
_t5, _vol = _c.get("t5"), _c.get("vol5")
|
|
||||||
_score = (None if _ch is None or _t5 is None or _vol is None
|
|
||||||
else round(_ch * (_t5 + 0.4 * _vol), 4))
|
|
||||||
_disc = _a.get("disc_now_approx")
|
|
||||||
_rows.append({
|
_rows.append({
|
||||||
"sym": _s.upper(),
|
"sym": _s,
|
||||||
"name": (_a.get("name") or _c.get("name") or "")[:44],
|
"name": (_univ.get(_s, {}).get("name") or "")[:44],
|
||||||
"char": _ch,
|
"char": _ch,
|
||||||
"actual": _actual,
|
|
||||||
"disc%": None if _disc is None else round(100 * _disc, 1),
|
"disc%": None if _disc is None else round(100 * _disc, 1),
|
||||||
"t5%": None if _t5 is None else round(100 * _t5, 1),
|
"t5%": None if _t5 is None else round(100 * _t5, 1),
|
||||||
"vol5%": None if _vol is None else round(100 * _vol, 1),
|
"vol5%": None if _vol is None else round(100 * _vol, 1),
|
||||||
"pos_scen": _c.get("n_pos_scen"),
|
"pos_scen": _npos,
|
||||||
"tenders": _a.get("n_tender"),
|
"tenders": _tend,
|
||||||
"score": _score})
|
"score": _score})
|
||||||
_cef_df = _pd.DataFrame(_rows)
|
_cef_df = _pd.DataFrame(_rows)
|
||||||
st.divider()
|
st.divider()
|
||||||
st.subheader("Closed-end funds — tax-arb shortlist")
|
st.subheader("Closed-end funds — tax-arb ranking")
|
||||||
st.dataframe(
|
st.dataframe(_cef_df, width="stretch", height=460)
|
||||||
_cef_df.sort_values("score", ascending=False, na_position="last"),
|
|
||||||
width="stretch", height=460)
|
|
||||||
st.caption(
|
st.caption(
|
||||||
"char = distribution character 0–1 (0 = all ordinary income, 1 = "
|
f"{len(_cef_df)} CEFs with VERIFIED per-share distribution "
|
||||||
"all capital-gains/ROC). actual = verified from the fund's own "
|
"character (divs + gains + ROC == total distributions per year, "
|
||||||
"per-share financial highlights (divs + gains + ROC == total "
|
"plus the NAV chain, from the fund's own report). char 0–1: "
|
||||||
"distributions per year, plus the full NAV chain); otherwise the "
|
"0 = all ordinary income, 1 = all capital-gains/ROC (taxable "
|
||||||
"return-sleeve model. disc% = market vs NAV. pos_scen = severe-"
|
"income that defers to the LTCG/ROC rate in a taxable account). "
|
||||||
"drawdown scenarios with a positive 5y return. score = char × "
|
"score = char × (5y total return + 0.4 × 5y vol): harvestable "
|
||||||
"(5y total return + 0.4 × 5y vol) — harvestable character "
|
"character weighted by how much damage a forced sale could do. "
|
||||||
"weighted by damage potential.")
|
"tenders = N-23C repurchase/tender filings (sponsor capital-"
|
||||||
|
"management pressure). disc% = FY-end market vs NAV.")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user