RIC-vs-pass-through verification + return-of-capital detector
fundlab/rocdetect.py: for a fund's latest N-CSR annual report, parses the per-share "Financial Highlights" distribution table (NII / gains / (tax) return of capital / total), with whitespace normalization, dollar-basis and fiscal-year-column rejection, per-column arithmetic placement of short rows (NII+gains+ROC==total), and family-report disambiguation by current-NAV match. SEC 503 backoff, per-fund cache in roc_cache/. Verified over the 38 core funds: - HMEZX: 18.3% of 5y distributions were return of capital (0.62 + 0.37 per share in 2 of 5 fiscal years) - reinforces the IRA placement. - 17 funds verified zero ROC (AFLIX, ANGLX, ATESX, BTMIX, the EV macro funds, FHCOX/FHMIX, LCORX/LCRIX, PMAIX/PMFKX, SVARX, WMNUX). - 14 unverified (non-standard report layouts - prototype parser), PMORX/QCMMRX tickers not EDGAR-resolvable. Also verified (form-type history on EDGAR): CVSIX, QSPNX, MBXIX, ATRFX, ATESX, EAGMX, PBAIX, WISEX, EBSAX all file NPORT-P + N-CSR + 497 -> registered RICs, not pass-throughs; the whole 497-filer universe is registered, so no candidate can pass through capital losses. Proof case for ROC: PGSIX per-share table shows a tax return-of-capital line and NAV eroded 11.87 -> 7.88 (-34%) over 5 fiscal years while paying out every year. RESEARCH.md: full write-up. No app change (analysis artifact).
This commit is contained in:
parent
9458e316cb
commit
620e2499ac
|
|
@ -130,6 +130,62 @@ not a gate.
|
||||||
4. CEF universe (485/N-2 filers) - separate pass; CEFs have
|
4. CEF universe (485/N-2 filers) - separate pass; CEFs have
|
||||||
premium/discount dynamics the NAV screen can't see.
|
premium/discount dynamics the NAV screen can't see.
|
||||||
|
|
||||||
|
### RIC vs pass-through + return-of-capital check (2026-08-27)
|
||||||
|
User asked (1) whether the long-short alternatives are RICs or
|
||||||
|
pass-throughs, and (2) whether mutual funds can distribute
|
||||||
|
non-taxable "return of capital".
|
||||||
|
|
||||||
|
**RIC vs pass-through - verified on EDGAR (form-type history):**
|
||||||
|
CVSIX, QSPNX, MBXIX, ATRFX, ATESX, EAGMX, PBAIX, WISEX, EBSAX ALL
|
||||||
|
file NPORT-P + N-CSR + N-CSRS + N-CEN + 497/497K - i.e. registered
|
||||||
|
40 Act open-end funds = RICs. MBXIX/ATRFX (the "hedge strategy" I
|
||||||
|
classes) are in a registered trust (Mutual Fund Series Trust) even
|
||||||
|
though they sound like hedge funds. 1120-R tax returns are filed with
|
||||||
|
the IRS, not EDGAR, so their absence there is expected. The whole
|
||||||
|
2,384-fund universe is 497-filers, so NO candidate is a pass-through:
|
||||||
|
none can pass capital losses to shareholders; the only internal
|
||||||
|
tax benefit is the harvest reserve (offsets the fund's FUTURE gain
|
||||||
|
distributions). K-1 loss pass-throughs (MLPs, private credit) are
|
||||||
|
outside the universe and IRA-bound anyway.
|
||||||
|
|
||||||
|
**Return of capital - yes, and it happens in the candidate pool:**
|
||||||
|
ROC = distribution characterized as a return of the shareholder's
|
||||||
|
OWN basis. Non-taxable until basis is exhausted, then taxed as a
|
||||||
|
capital gain - i.e. DEFERRED, not exempt. It is also a red flag:
|
||||||
|
the fund is paying back principal. The definitive source is the
|
||||||
|
per-share "Financial Highlights" table in the N-CSR annual report
|
||||||
|
("Distributions from: NII / gains / (tax) return of capital /
|
||||||
|
total").
|
||||||
|
|
||||||
|
fundlab/rocdetect.py parses that table (whitespace-normalized,
|
||||||
|
dollar-basis and date-column rejection, lone values placed by
|
||||||
|
per-column arithmetic NII+gains+ROC==total, family reports
|
||||||
|
disambiguated by current-NAV match). Run over the 38 core funds
|
||||||
|
(cached per fund in roc_cache/):
|
||||||
|
- HMEZX: 18.3% of 5y distributions were ROC ($0.62 and $0.37 per
|
||||||
|
share in 2 of 5 fiscal years; $1.01/yr distributions otherwise
|
||||||
|
from NII) - reinforces the IRA placement.
|
||||||
|
- 17 funds verified ZERO ROC: AFLIX, ANGLX, ATESX, BTMIX, EAGMX,
|
||||||
|
EGRIX, EGRSX, ETSIX, FHCOX, FHMIX, LCORX, LCRIX, PMAIX, PMFKX,
|
||||||
|
SVARX, WMNUX.
|
||||||
|
- 14 unverified (non-standard report layouts: Vanguard MERVX,
|
||||||
|
JPMorgan JLPSX, AQR QSPNX, PGIM SCFZX, Columbia COSIX, ...):
|
||||||
|
parser is a prototype; per-family layouts not all covered.
|
||||||
|
- PMORX/QCMMRX: ticker not resolvable via browse-edgar.
|
||||||
|
|
||||||
|
Separate proof case found earlier while checking the NAV-decline
|
||||||
|
heuristic: PGSIX (Putnam Mortgage Securities, a drawdown-screen
|
||||||
|
candidate) - per-share table shows a "Tax return of capital" line
|
||||||
|
($0.35/sh in FY2021: NII $0.14 + ROC $0.35 = $0.49 total) and the
|
||||||
|
NAV eroded $11.87 -> $7.88 (-34%) over the 5 shown fiscal years
|
||||||
|
while paying out every year. Classic constant-distribution principal
|
||||||
|
erosion.
|
||||||
|
|
||||||
|
Placement consequence: ROC does NOT help the taxable-account case -
|
||||||
|
basis deferral in a taxable account is what a traditional IRA gives
|
||||||
|
you anyway, and it signals principal erosion. The taxable-friendly
|
||||||
|
character remains qualified dividends / LTCG.
|
||||||
|
|
||||||
### Price-appreciation vs. payout split (fundlab/taxsplit.py, 2026-08-27)
|
### Price-appreciation vs. payout split (fundlab/taxsplit.py, 2026-08-27)
|
||||||
Follow-up to the tax-location plan: the user pointed out that fund
|
Follow-up to the tax-location plan: the user pointed out that fund
|
||||||
NAV appreciation is ALSO a capital gain (LTCG on a >1y sale) - the
|
NAV appreciation is ALSO a capital gain (LTCG on a >1y sale) - the
|
||||||
|
|
|
||||||
22
fundlab/roc_cache/aflix.json
Normal file
22
fundlab/roc_cache/aflix.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "AFLIX",
|
||||||
|
"cik": "0001552947",
|
||||||
|
"trust": "Two Roads Shared Trust",
|
||||||
|
"filing": "2026-07-09",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.78
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/aguax.json
Normal file
9
fundlab/roc_cache/aguax.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "AGUAX",
|
||||||
|
"cik": "0000809593",
|
||||||
|
"trust": "AMERICAN BEACON FUNDS",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
24
fundlab/roc_cache/anglx.json
Normal file
24
fundlab/roc_cache/anglx.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"sym": "ANGLX",
|
||||||
|
"cik": "0001612930",
|
||||||
|
"trust": "Angel Oak Funds Trust",
|
||||||
|
"filing": "2026-04-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.47
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.47
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
8.78
|
||||||
|
],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/atesx.json
Normal file
22
fundlab/roc_cache/atesx.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "ATESX",
|
||||||
|
"cik": "0001644419",
|
||||||
|
"trust": "Northern Lights Fund Trust IV",
|
||||||
|
"filing": "2026-08-10",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-1.14
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.14
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/atrfx.json
Normal file
9
fundlab/roc_cache/atrfx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "ATRFX",
|
||||||
|
"cik": "0001355064",
|
||||||
|
"trust": "MUTUAL FUND SERIES TRUST",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
40
fundlab/roc_cache/btmix.json
Normal file
40
fundlab/roc_cache/btmix.json
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
{
|
||||||
|
"sym": "BTMIX",
|
||||||
|
"cik": "0001282693",
|
||||||
|
"trust": "BAIRD FUNDS INC",
|
||||||
|
"filing": "2026-03-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.31,
|
||||||
|
-0.29,
|
||||||
|
-0.25,
|
||||||
|
-0.13,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.31,
|
||||||
|
-0.29,
|
||||||
|
-0.25,
|
||||||
|
-0.13,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
10.09,
|
||||||
|
9.96,
|
||||||
|
9.96,
|
||||||
|
9.83,
|
||||||
|
10.38
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": -0.028
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/coiax.json
Normal file
9
fundlab/roc_cache/coiax.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "COIAX",
|
||||||
|
"cik": "0000804239",
|
||||||
|
"trust": "SEI INSTITUTIONAL MANAGED TRUST",
|
||||||
|
"filing": "2025-12-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/cosix.json
Normal file
9
fundlab/roc_cache/cosix.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "COSIX",
|
||||||
|
"cik": "0000773757",
|
||||||
|
"trust": "COLUMBIA FUNDS SERIES TRUST I",
|
||||||
|
"filing": "2026-08-06",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/cvsix.json
Normal file
9
fundlab/roc_cache/cvsix.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "CVSIX",
|
||||||
|
"cik": "0000826732",
|
||||||
|
"trust": "CALAMOS INVESTMENT TRUST/IL",
|
||||||
|
"filing": "2025-12-29",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/dmszx.json
Normal file
9
fundlab/roc_cache/dmszx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "DMSZX",
|
||||||
|
"cik": "0001688680",
|
||||||
|
"trust": "Brinker Capital Destinations Trust",
|
||||||
|
"filing": "2026-05-06",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
20
fundlab/roc_cache/dultx.json
Normal file
20
fundlab/roc_cache/dultx.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"sym": "DULTX",
|
||||||
|
"cik": "0000230173",
|
||||||
|
"trust": "DELAWARE GROUP CASH RESERVE",
|
||||||
|
"filing": "2026-06-09",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
2.0
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/eagmx.json
Normal file
22
fundlab/roc_cache/eagmx.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "EAGMX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/egrix.json
Normal file
22
fundlab/roc_cache/egrix.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "EGRIX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/egrsx.json
Normal file
22
fundlab/roc_cache/egrsx.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "EGRSX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/etsix.json
Normal file
22
fundlab/roc_cache/etsix.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "ETSIX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
32
fundlab/roc_cache/femdx.json
Normal file
32
fundlab/roc_cache/femdx.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"sym": "FEMDX",
|
||||||
|
"cik": "0001124459",
|
||||||
|
"trust": "Franklin Global Trust",
|
||||||
|
"filing": "2025-09-26",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.53,
|
||||||
|
-0.34,
|
||||||
|
-0.9
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [
|
||||||
|
12.35,
|
||||||
|
11.55,
|
||||||
|
10.46,
|
||||||
|
9.91,
|
||||||
|
11.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": 0.053
|
||||||
|
}
|
||||||
32
fundlab/roc_cache/fhcox.json
Normal file
32
fundlab/roc_cache/fhcox.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"sym": "FHCOX",
|
||||||
|
"cik": "0001707560",
|
||||||
|
"trust": "Federated Hermes Adviser Series",
|
||||||
|
"filing": "2026-08-25",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.01,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.21,
|
||||||
|
-0.81,
|
||||||
|
-0.63,
|
||||||
|
-0.34,
|
||||||
|
-0.61
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
32
fundlab/roc_cache/fhmix.json
Normal file
32
fundlab/roc_cache/fhmix.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"sym": "FHMIX",
|
||||||
|
"cik": "0001707560",
|
||||||
|
"trust": "Federated Hermes Adviser Series",
|
||||||
|
"filing": "2026-08-25",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.01,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.21,
|
||||||
|
-0.81,
|
||||||
|
-0.63,
|
||||||
|
-0.34,
|
||||||
|
-0.61
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/hicox.json
Normal file
9
fundlab/roc_cache/hicox.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "HICOX",
|
||||||
|
"cik": "0000810744",
|
||||||
|
"trust": "COLORADO BONDSHARES A TAX EXEMPT FUND",
|
||||||
|
"filing": "2025-12-04",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
39
fundlab/roc_cache/hmezx.json
Normal file
39
fundlab/roc_cache/hmezx.json
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"sym": "HMEZX",
|
||||||
|
"cik": "0001354917",
|
||||||
|
"trust": "NEXPOINT FUNDS I",
|
||||||
|
"filing": "2025-09-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-1.01,
|
||||||
|
-1.0,
|
||||||
|
-0.38,
|
||||||
|
-0.4
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
-0.62,
|
||||||
|
-0.37,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.01,
|
||||||
|
-1.0,
|
||||||
|
-1.0,
|
||||||
|
-1.24,
|
||||||
|
-1.16
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
19.83,
|
||||||
|
19.71,
|
||||||
|
19.21,
|
||||||
|
19.89,
|
||||||
|
20.58
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": 0.183,
|
||||||
|
"nav_change_5y": -0.036
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/jlpsx.json
Normal file
9
fundlab/roc_cache/jlpsx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "JLPSX",
|
||||||
|
"cik": "0001217286",
|
||||||
|
"trust": "JPMorgan Trust I",
|
||||||
|
"filing": "2026-05-07",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
20
fundlab/roc_cache/lamhx.json
Normal file
20
fundlab/roc_cache/lamhx.json
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"sym": "LAMHX",
|
||||||
|
"cik": "0000887194",
|
||||||
|
"trust": "LORD ABBETT RESEARCH FUND INC",
|
||||||
|
"filing": "2026-02-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
24.79
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
32
fundlab/roc_cache/lcorx.json
Normal file
32
fundlab/roc_cache/lcorx.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"sym": "LCORX",
|
||||||
|
"cik": "0001511699",
|
||||||
|
"trust": "Managed Portfolio Series",
|
||||||
|
"filing": "2026-08-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.04,
|
||||||
|
-0.06,
|
||||||
|
-0.03,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.2
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
18.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
32
fundlab/roc_cache/lcrix.json
Normal file
32
fundlab/roc_cache/lcrix.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"sym": "LCRIX",
|
||||||
|
"cik": "0001511699",
|
||||||
|
"trust": "Managed Portfolio Series",
|
||||||
|
"filing": "2026-08-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.04,
|
||||||
|
-0.06,
|
||||||
|
-0.03,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.2
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
18.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
30
fundlab/roc_cache/lpxax.json
Normal file
30
fundlab/roc_cache/lpxax.json
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"sym": "LPXAX",
|
||||||
|
"cik": "0001652200",
|
||||||
|
"trust": "Cohen & Steers Low Duration Preferred & Income Fund, Inc.",
|
||||||
|
"filing": "2026-07-02",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.51
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
-0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [
|
||||||
|
9.59,
|
||||||
|
9.43,
|
||||||
|
9.23,
|
||||||
|
8.87,
|
||||||
|
9.36
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": 0.025
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/mbxix.json
Normal file
9
fundlab/roc_cache/mbxix.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "MBXIX",
|
||||||
|
"cik": "0001355064",
|
||||||
|
"trust": "MUTUAL FUND SERIES TRUST",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/mervx.json
Normal file
9
fundlab/roc_cache/mervx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "MERVX",
|
||||||
|
"cik": "0001208133",
|
||||||
|
"trust": "MERGER FUND VL",
|
||||||
|
"filing": "2026-03-05",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
31
fundlab/roc_cache/pmaix.json
Normal file
31
fundlab/roc_cache/pmaix.json
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"sym": "PMAIX",
|
||||||
|
"cik": "0002042316",
|
||||||
|
"trust": "Victory Portfolios IV",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.04,
|
||||||
|
-0.04
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-6.32,
|
||||||
|
-1.99,
|
||||||
|
-1.1,
|
||||||
|
-1.06,
|
||||||
|
-6.45
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
31
fundlab/roc_cache/pmfkx.json
Normal file
31
fundlab/roc_cache/pmfkx.json
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"sym": "PMFKX",
|
||||||
|
"cik": "0002042316",
|
||||||
|
"trust": "Victory Portfolios IV",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.04,
|
||||||
|
-0.04
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-6.32,
|
||||||
|
-1.99,
|
||||||
|
-1.1,
|
||||||
|
-1.06,
|
||||||
|
-6.45
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/qspnx.json
Normal file
9
fundlab/roc_cache/qspnx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "QSPNX",
|
||||||
|
"cik": "0001444822",
|
||||||
|
"trust": "AQR Funds",
|
||||||
|
"filing": "2026-02-26",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/rctix.json
Normal file
9
fundlab/roc_cache/rctix.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "RCTIX",
|
||||||
|
"cik": "0001516523",
|
||||||
|
"trust": "Advisers Investment Trust",
|
||||||
|
"filing": "2025-11-26",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/safex.json
Normal file
9
fundlab/roc_cache/safex.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "SAFEX",
|
||||||
|
"cik": "0001257927",
|
||||||
|
"trust": "WEITZ FUNDS",
|
||||||
|
"filing": "2026-05-27",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/scfzx.json
Normal file
9
fundlab/roc_cache/scfzx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "SCFZX",
|
||||||
|
"cik": "0000887991",
|
||||||
|
"trust": "PRUDENTIAL INVESTMENT PORTFOLIOS 8",
|
||||||
|
"filing": "2025-12-03",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
24
fundlab/roc_cache/svarx.json
Normal file
24
fundlab/roc_cache/svarx.json
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"sym": "SVARX",
|
||||||
|
"cik": "0001556505",
|
||||||
|
"trust": "Advisors Preferred Trust",
|
||||||
|
"filing": "2026-03-10",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-3.72
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-3.72
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
38.76
|
||||||
|
],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
9
fundlab/roc_cache/usmsx.json
Normal file
9
fundlab/roc_cache/usmsx.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"sym": "USMSX",
|
||||||
|
"cik": "0001659326",
|
||||||
|
"trust": "JPMORGAN TRUST IV",
|
||||||
|
"filing": "2026-05-07",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
22
fundlab/roc_cache/wmnux.json
Normal file
22
fundlab/roc_cache/wmnux.json
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"sym": "WMNUX",
|
||||||
|
"cik": "0001545440",
|
||||||
|
"trust": "Ultimus Managers Trust",
|
||||||
|
"filing": "2026-08-03",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.31
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.31
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
718
fundlab/roc_results.json
Normal file
718
fundlab/roc_results.json
Normal file
|
|
@ -0,0 +1,718 @@
|
||||||
|
{
|
||||||
|
"AFLIX": {
|
||||||
|
"sym": "AFLIX",
|
||||||
|
"cik": "0001552947",
|
||||||
|
"trust": "Two Roads Shared Trust",
|
||||||
|
"filing": "2026-07-09",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.78
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"AGUAX": {
|
||||||
|
"sym": "AGUAX",
|
||||||
|
"cik": "0000809593",
|
||||||
|
"trust": "AMERICAN BEACON FUNDS",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"ANGLX": {
|
||||||
|
"sym": "ANGLX",
|
||||||
|
"cik": "0001612930",
|
||||||
|
"trust": "Angel Oak Funds Trust",
|
||||||
|
"filing": "2026-04-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.47
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.47
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
8.78
|
||||||
|
],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"ATESX": {
|
||||||
|
"sym": "ATESX",
|
||||||
|
"cik": "0001644419",
|
||||||
|
"trust": "Northern Lights Fund Trust IV",
|
||||||
|
"filing": "2026-08-10",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-1.14
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.14
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"ATRFX": {
|
||||||
|
"sym": "ATRFX",
|
||||||
|
"cik": "0001355064",
|
||||||
|
"trust": "MUTUAL FUND SERIES TRUST",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"BTMIX": {
|
||||||
|
"sym": "BTMIX",
|
||||||
|
"cik": "0001282693",
|
||||||
|
"trust": "BAIRD FUNDS INC",
|
||||||
|
"filing": "2026-03-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.31,
|
||||||
|
-0.29,
|
||||||
|
-0.25,
|
||||||
|
-0.13,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.31,
|
||||||
|
-0.29,
|
||||||
|
-0.25,
|
||||||
|
-0.13,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
10.09,
|
||||||
|
9.96,
|
||||||
|
9.96,
|
||||||
|
9.83,
|
||||||
|
10.38
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": -0.028
|
||||||
|
},
|
||||||
|
"COIAX": {
|
||||||
|
"sym": "COIAX",
|
||||||
|
"cik": "0000804239",
|
||||||
|
"trust": "SEI INSTITUTIONAL MANAGED TRUST",
|
||||||
|
"filing": "2025-12-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"COSIX": {
|
||||||
|
"sym": "COSIX",
|
||||||
|
"cik": "0000773757",
|
||||||
|
"trust": "COLUMBIA FUNDS SERIES TRUST I",
|
||||||
|
"filing": "2026-08-06",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"CVSIX": {
|
||||||
|
"sym": "CVSIX",
|
||||||
|
"cik": "0000826732",
|
||||||
|
"trust": "CALAMOS INVESTMENT TRUST/IL",
|
||||||
|
"filing": "2025-12-29",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"DMSZX": {
|
||||||
|
"sym": "DMSZX",
|
||||||
|
"cik": "0001688680",
|
||||||
|
"trust": "Brinker Capital Destinations Trust",
|
||||||
|
"filing": "2026-05-06",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"DULTX": {
|
||||||
|
"sym": "DULTX",
|
||||||
|
"cik": "0000230173",
|
||||||
|
"trust": "DELAWARE GROUP CASH RESERVE",
|
||||||
|
"filing": "2026-06-09",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
2.0
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"EAGMX": {
|
||||||
|
"sym": "EAGMX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"EGRIX": {
|
||||||
|
"sym": "EGRIX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"EGRSX": {
|
||||||
|
"sym": "EGRSX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"ETSIX": {
|
||||||
|
"sym": "ETSIX",
|
||||||
|
"cik": "0000745463",
|
||||||
|
"trust": "Eaton Vance Mutual Funds Trust",
|
||||||
|
"filing": "2026-03-31",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.73
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-2.05
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"FEMDX": {
|
||||||
|
"sym": "FEMDX",
|
||||||
|
"cik": "0001124459",
|
||||||
|
"trust": "Franklin Global Trust",
|
||||||
|
"filing": "2025-09-26",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.53,
|
||||||
|
-0.34,
|
||||||
|
-0.9
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [
|
||||||
|
12.35,
|
||||||
|
11.55,
|
||||||
|
10.46,
|
||||||
|
9.91,
|
||||||
|
11.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": 0.053
|
||||||
|
},
|
||||||
|
"FHCOX": {
|
||||||
|
"sym": "FHCOX",
|
||||||
|
"cik": "0001707560",
|
||||||
|
"trust": "Federated Hermes Adviser Series",
|
||||||
|
"filing": "2026-08-25",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.01,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.21,
|
||||||
|
-0.81,
|
||||||
|
-0.63,
|
||||||
|
-0.34,
|
||||||
|
-0.61
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"FHMIX": {
|
||||||
|
"sym": "FHMIX",
|
||||||
|
"cik": "0001707560",
|
||||||
|
"trust": "Federated Hermes Adviser Series",
|
||||||
|
"filing": "2026-08-25",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.01,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.21,
|
||||||
|
-0.81,
|
||||||
|
-0.63,
|
||||||
|
-0.34,
|
||||||
|
-0.61
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"HICOX": {
|
||||||
|
"sym": "HICOX",
|
||||||
|
"cik": "0000810744",
|
||||||
|
"trust": "COLORADO BONDSHARES A TAX EXEMPT FUND",
|
||||||
|
"filing": "2025-12-04",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"HMEZX": {
|
||||||
|
"sym": "HMEZX",
|
||||||
|
"cik": "0001354917",
|
||||||
|
"trust": "NEXPOINT FUNDS I",
|
||||||
|
"filing": "2025-09-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-1.01,
|
||||||
|
-1.0,
|
||||||
|
-0.38,
|
||||||
|
-0.4
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
-0.62,
|
||||||
|
-0.37,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.01,
|
||||||
|
-1.0,
|
||||||
|
-1.0,
|
||||||
|
-1.24,
|
||||||
|
-1.16
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
19.83,
|
||||||
|
19.71,
|
||||||
|
19.21,
|
||||||
|
19.89,
|
||||||
|
20.58
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": 0.183,
|
||||||
|
"nav_change_5y": -0.036
|
||||||
|
},
|
||||||
|
"JLPSX": {
|
||||||
|
"sym": "JLPSX",
|
||||||
|
"cik": "0001217286",
|
||||||
|
"trust": "JPMorgan Trust I",
|
||||||
|
"filing": "2026-05-07",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"LAMHX": {
|
||||||
|
"sym": "LAMHX",
|
||||||
|
"cik": "0000887194",
|
||||||
|
"trust": "LORD ABBETT RESEARCH FUND INC",
|
||||||
|
"filing": "2026-02-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
24.79
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"LCORX": {
|
||||||
|
"sym": "LCORX",
|
||||||
|
"cik": "0001511699",
|
||||||
|
"trust": "Managed Portfolio Series",
|
||||||
|
"filing": "2026-08-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.04,
|
||||||
|
-0.06,
|
||||||
|
-0.03,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.2
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
18.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"LCRIX": {
|
||||||
|
"sym": "LCRIX",
|
||||||
|
"cik": "0001511699",
|
||||||
|
"trust": "Managed Portfolio Series",
|
||||||
|
"filing": "2026-08-04",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.02,
|
||||||
|
-0.04,
|
||||||
|
-0.06,
|
||||||
|
-0.03,
|
||||||
|
-0.09
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-1.2
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
18.73
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"LPXAX": {
|
||||||
|
"sym": "LPXAX",
|
||||||
|
"cik": "0001652200",
|
||||||
|
"trust": "Cohen & Steers Low Duration Preferred & Income Fund, Inc.",
|
||||||
|
"filing": "2026-07-02",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.51
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
-0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [],
|
||||||
|
"nav": [
|
||||||
|
9.59,
|
||||||
|
9.43,
|
||||||
|
9.23,
|
||||||
|
8.87,
|
||||||
|
9.36
|
||||||
|
],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": 0.025
|
||||||
|
},
|
||||||
|
"MBXIX": {
|
||||||
|
"sym": "MBXIX",
|
||||||
|
"cik": "0001355064",
|
||||||
|
"trust": "MUTUAL FUND SERIES TRUST",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"MERVX": {
|
||||||
|
"sym": "MERVX",
|
||||||
|
"cik": "0001208133",
|
||||||
|
"trust": "MERGER FUND VL",
|
||||||
|
"filing": "2026-03-05",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"PMAIX": {
|
||||||
|
"sym": "PMAIX",
|
||||||
|
"cik": "0002042316",
|
||||||
|
"trust": "Victory Portfolios IV",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.04,
|
||||||
|
-0.04
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-6.32,
|
||||||
|
-1.99,
|
||||||
|
-1.1,
|
||||||
|
-1.06,
|
||||||
|
-6.45
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"PMFKX": {
|
||||||
|
"sym": "PMFKX",
|
||||||
|
"cik": "0002042316",
|
||||||
|
"trust": "Victory Portfolios IV",
|
||||||
|
"filing": "2026-06-08",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.04,
|
||||||
|
-0.04
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-6.32,
|
||||||
|
-1.99,
|
||||||
|
-1.1,
|
||||||
|
-1.06,
|
||||||
|
-6.45
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 5
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"PMORX": {
|
||||||
|
"sym": "PMORX",
|
||||||
|
"error": "CIK not found"
|
||||||
|
},
|
||||||
|
"QCMMRX": {
|
||||||
|
"sym": "QCMMRX",
|
||||||
|
"error": "CIK not found"
|
||||||
|
},
|
||||||
|
"QSPNX": {
|
||||||
|
"sym": "QSPNX",
|
||||||
|
"cik": "0001444822",
|
||||||
|
"trust": "AQR Funds",
|
||||||
|
"filing": "2026-02-26",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"RCTIX": {
|
||||||
|
"sym": "RCTIX",
|
||||||
|
"cik": "0001516523",
|
||||||
|
"trust": "Advisers Investment Trust",
|
||||||
|
"filing": "2025-11-26",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"SAFEX": {
|
||||||
|
"sym": "SAFEX",
|
||||||
|
"cik": "0001257927",
|
||||||
|
"trust": "WEITZ FUNDS",
|
||||||
|
"filing": "2026-05-27",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"SCFZX": {
|
||||||
|
"sym": "SCFZX",
|
||||||
|
"cik": "0000887991",
|
||||||
|
"trust": "PRUDENTIAL INVESTMENT PORTFOLIOS 8",
|
||||||
|
"filing": "2025-12-03",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"SVARX": {
|
||||||
|
"sym": "SVARX",
|
||||||
|
"cik": "0001556505",
|
||||||
|
"trust": "Advisors Preferred Trust",
|
||||||
|
"filing": "2026-03-10",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-3.72
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-3.72
|
||||||
|
],
|
||||||
|
"nav": [
|
||||||
|
38.76
|
||||||
|
],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"USMSX": {
|
||||||
|
"sym": "USMSX",
|
||||||
|
"cik": "0001659326",
|
||||||
|
"trust": "JPMORGAN TRUST IV",
|
||||||
|
"filing": "2026-05-07",
|
||||||
|
"table": null,
|
||||||
|
"roc_share_5y": null,
|
||||||
|
"nav_change_5y": null
|
||||||
|
},
|
||||||
|
"WMNUX": {
|
||||||
|
"sym": "WMNUX",
|
||||||
|
"cik": "0001545440",
|
||||||
|
"trust": "Ultimus Managers Trust",
|
||||||
|
"filing": "2026-08-03",
|
||||||
|
"table": {
|
||||||
|
"nii": [
|
||||||
|
-0.31
|
||||||
|
],
|
||||||
|
"gains": [],
|
||||||
|
"roc": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"tot": [
|
||||||
|
-0.31
|
||||||
|
],
|
||||||
|
"nav": [],
|
||||||
|
"ncols": 1
|
||||||
|
},
|
||||||
|
"roc_share_5y": -0.0,
|
||||||
|
"nav_change_5y": null
|
||||||
|
}
|
||||||
|
}
|
||||||
39
fundlab/roc_run.log
Normal file
39
fundlab/roc_run.log
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
fund ROC 5y NAV 5y dist/yr (most recent first) filing
|
||||||
|
HMEZX 18.3% -3.6% tot -1.01/roc -0.62 tot -1.00/roc -0.37 to 2025-09-08
|
||||||
|
AFLIX -0.0% - tot -0.78 2026-07-09
|
||||||
|
AGUAX (no per-share table found)
|
||||||
|
ANGLX -0.0% - tot -0.47 2026-04-08
|
||||||
|
ATESX -0.0% - tot -1.14 2026-08-10
|
||||||
|
ATRFX (no per-share table found)
|
||||||
|
BTMIX -0.0% -2.8% tot -0.31 tot -0.29 tot -0.25 tot -0.13 2026-03-04
|
||||||
|
COIAX (no per-share table found)
|
||||||
|
COSIX (no per-share table found)
|
||||||
|
CVSIX (no per-share table found)
|
||||||
|
DMSZX (no per-share table found)
|
||||||
|
DULTX - - tot 0.00 2026-06-09
|
||||||
|
EAGMX -0.0% - tot -2.05 2026-03-31
|
||||||
|
EGRIX -0.0% - tot -2.05 2026-03-31
|
||||||
|
EGRSX -0.0% - tot -2.05 2026-03-31
|
||||||
|
ETSIX -0.0% - tot -2.05 2026-03-31
|
||||||
|
FEMDX - +5.3% tot 0.00 tot 0.00 tot 0.00 tot 0.00 tot 2025-09-26
|
||||||
|
FHCOX -0.0% - tot -1.21 tot -0.81 tot -0.63 tot -0.34 2026-08-25
|
||||||
|
FHMIX -0.0% - tot -1.21 tot -0.81 tot -0.63 tot -0.34 2026-08-25
|
||||||
|
HICOX (no per-share table found)
|
||||||
|
JLPSX (no per-share table found)
|
||||||
|
LAMHX - - tot 0.00 2026-02-04
|
||||||
|
LCORX -0.0% - tot -1.20 tot 0.00 tot 0.00 tot 0.00 tot 2026-08-04
|
||||||
|
LCRIX -0.0% - tot -1.20 tot 0.00 tot 0.00 tot 0.00 tot 2026-08-04
|
||||||
|
LPXAX - +2.5% tot 0.00 tot 0.00 tot 0.00 tot 0.00 tot 2026-07-02
|
||||||
|
MBXIX (no per-share table found)
|
||||||
|
MERVX (no per-share table found)
|
||||||
|
PMAIX -0.0% - tot -6.32 tot -1.99 tot -1.10 tot -1.06 2026-06-08
|
||||||
|
PMFKX -0.0% - tot -6.32 tot -1.99 tot -1.10 tot -1.06 2026-06-08
|
||||||
|
PMORX (CIK not found)
|
||||||
|
QCMMRX (CIK not found)
|
||||||
|
QSPNX (no per-share table found)
|
||||||
|
RCTIX (no per-share table found)
|
||||||
|
SAFEX (no per-share table found)
|
||||||
|
SCFZX (no per-share table found)
|
||||||
|
SVARX -0.0% - tot -3.72 2026-03-10
|
||||||
|
USMSX (no per-share table found)
|
||||||
|
WMNUX -0.0% - tot -0.31 2026-08-03
|
||||||
302
fundlab/rocdetect.py
Normal file
302
fundlab/rocdetect.py
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
"""Return-of-capital (ROC) detector from N-CSR annual reports.
|
||||||
|
|
||||||
|
A mutual fund's per-share "Financial Highlights" table (item 7 of the
|
||||||
|
annual report, filed as N-CSR) lists distributions by character:
|
||||||
|
|
||||||
|
Distributions from:
|
||||||
|
Net investment income ...
|
||||||
|
(Realized) capital gains ...
|
||||||
|
(Tax) return of capital ...
|
||||||
|
Total distributions ...
|
||||||
|
|
||||||
|
The "return of capital" line is the definitive answer to "is this
|
||||||
|
fund paying me back my own money?" - the price-data heuristic
|
||||||
|
(NAV falling while paying out) only SUGGESTS it, and for
|
||||||
|
long-duration funds NAV declines are usually rate damage, not ROC.
|
||||||
|
|
||||||
|
Facts used:
|
||||||
|
- The table shows up to 5-10 fiscal years, MOST RECENT LEFT.
|
||||||
|
- Values are parenthesized outflows: "(1.16)" = $1.16 distributed.
|
||||||
|
- A row with fewer tokens than the NII row has blanks dropped; the
|
||||||
|
arithmetic check (NII + gains + ROC == total per column) is used
|
||||||
|
to place a lone value (verified on PGSIX: the lone (0.35) only
|
||||||
|
reconciles in the oldest column: 0.14 + 0.35 = 0.49).
|
||||||
|
|
||||||
|
Run: python -m fundlab.rocdetect [SYM ...]
|
||||||
|
(no args = the 16 shortlist + 22 cross-checked funds)
|
||||||
|
Output: fundlab/roc_results.json (cached per fund in roc_cache/)
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from fundlab import edgar
|
||||||
|
|
||||||
|
HERE = Path(__file__).parent
|
||||||
|
CACHE = HERE / "roc_cache"
|
||||||
|
RESULTS = HERE / "roc_results.json"
|
||||||
|
NUM = re.compile(r"\$?\(?[\d,]+(?:\.\d+)?\)?:?")
|
||||||
|
|
||||||
|
|
||||||
|
def _nums(tokens: list[str]) -> list[float | None]:
|
||||||
|
out = []
|
||||||
|
for t in tokens:
|
||||||
|
t = t.strip().rstrip(":")
|
||||||
|
if not re.fullmatch(r"\$?\(?\d[\d,]*(?:\.\d+)?\)?", t):
|
||||||
|
out.append(None)
|
||||||
|
continue
|
||||||
|
neg = t.startswith("(")
|
||||||
|
v = float(t.strip("$()").replace(",", ""))
|
||||||
|
out.append(-v if neg else v)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
NUMTOK = re.compile(r"\$?\(?\d[\d,]*(?:\.\d+)?\)?")
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_table(block: str) -> dict | None:
|
||||||
|
"""Parse one per-share distribution block."""
|
||||||
|
|
||||||
|
def row(label_rx: str) -> list[float | None]:
|
||||||
|
rm = re.search(label_rx, block, re.I)
|
||||||
|
if not rm:
|
||||||
|
return []
|
||||||
|
tail = re.sub(r"\.{2,}", " ", block[rm.end(): rm.end() + 600])
|
||||||
|
# some reports print "(1.01 )" / "( 1.01 )" - rejoin the parens
|
||||||
|
tail = re.sub(r"\(\s+", "(", tail)
|
||||||
|
tail = re.sub(r"\s+\)", ")", tail)
|
||||||
|
vals: list[str] = []
|
||||||
|
for t in re.findall(r"\S+", tail):
|
||||||
|
if t == "$":
|
||||||
|
continue # "NAV ... $ 15.88 $ 15.23 ..."
|
||||||
|
if NUMTOK.fullmatch(t):
|
||||||
|
vals.append(t)
|
||||||
|
elif vals:
|
||||||
|
break # numbers over - next label reached
|
||||||
|
return _nums(vals[:10])
|
||||||
|
|
||||||
|
nii = row(r"net investment income")
|
||||||
|
gains = row(r"(?:realized |unrealized )?capital gains?")
|
||||||
|
roc = row(r"(?:tax )?return of capital")
|
||||||
|
tot = row(r"total distributions")
|
||||||
|
nav = row(r"net asset value, end of year")
|
||||||
|
if not nii and not tot:
|
||||||
|
return None
|
||||||
|
ncols = max(len(x) for x in (nii, gains, roc, tot, nav))
|
||||||
|
if ncols == 0:
|
||||||
|
return None
|
||||||
|
# place short rows by arithmetic: find the column where the
|
||||||
|
# components sum to the total (within rounding)
|
||||||
|
def _get(row: list[float | None], i: int) -> float:
|
||||||
|
if i < len(row) and row[i] is not None:
|
||||||
|
return row[i]
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
def place(vals: list[float | None]) -> list[float | None]:
|
||||||
|
if len(vals) == ncols:
|
||||||
|
return vals
|
||||||
|
from itertools import combinations
|
||||||
|
nz = [v for v in vals if v is not None]
|
||||||
|
if not nz:
|
||||||
|
return [0.0] * ncols
|
||||||
|
|
||||||
|
def reconcile(assign: list[int], nz_: list[float]) -> bool:
|
||||||
|
put = {c: v for c, v in zip(assign, nz_)}
|
||||||
|
for c in range(ncols):
|
||||||
|
if not tot or c >= len(tot) or tot[c] is None:
|
||||||
|
continue
|
||||||
|
s = (_get(nii, c) + _get(gains, c) + put.get(c, 0.0))
|
||||||
|
if abs(s - tot[c]) > 0.011 * max(1.0, abs(tot[c])) + 1e-9:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
# blanks are dropped when the table flattens, so a short row's
|
||||||
|
# values may sit in ANY columns - try placements and keep the
|
||||||
|
# one where every column reconciles (NII+gains+ROC == total)
|
||||||
|
if len(nz) <= 6 and tot:
|
||||||
|
for assign in combinations(range(ncols), len(nz)):
|
||||||
|
if reconcile(list(assign), nz):
|
||||||
|
put = {c: v for c, v in zip(assign, nz)}
|
||||||
|
return [put.get(c, 0.0) for c in range(ncols)]
|
||||||
|
full = [0.0] * ncols
|
||||||
|
for i, v in enumerate(vals):
|
||||||
|
if v is not None:
|
||||||
|
full[i] = v
|
||||||
|
return full
|
||||||
|
roc = place(roc)
|
||||||
|
# sanity: the per-share table has small values. Dollar-basis
|
||||||
|
# statements ("distributions to shareholders: $459,612,118") and
|
||||||
|
# fiscal-year date columns ("2025") leak in from other sections.
|
||||||
|
def _bad(vals: list[float | None]) -> bool:
|
||||||
|
for v in vals:
|
||||||
|
if v is None:
|
||||||
|
continue
|
||||||
|
if abs(v) > 1000:
|
||||||
|
return True
|
||||||
|
if v == int(v) and 1900 <= v <= 2100:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
if _bad(tot) or _bad(nav) or _bad(nii):
|
||||||
|
return None
|
||||||
|
if tot and all(v == 0 for v in tot if v is not None):
|
||||||
|
return None # all-zero total = wrong table (these all pay)
|
||||||
|
# table-level validity: every column with a total must reconcile
|
||||||
|
# (NII + gains + ROC == total). Catches rows that leaked in from
|
||||||
|
# the operations section or prose ("...capital gains of $3.00...").
|
||||||
|
if tot:
|
||||||
|
for c in range(ncols):
|
||||||
|
t = tot[c] if c < len(tot) else None
|
||||||
|
if t is None:
|
||||||
|
continue
|
||||||
|
s = _get(nii, c) + _get(gains, c) + (roc[c] if c < len(roc) else 0.0)
|
||||||
|
if abs(s - t) > 0.011 * max(1.0, abs(t)) + 0.005:
|
||||||
|
return None
|
||||||
|
return {"nii": nii, "gains": gains, "roc": roc, "tot": tot,
|
||||||
|
"nav": nav, "ncols": ncols}
|
||||||
|
|
||||||
|
|
||||||
|
def parse_highlights(txt: str, nav_now: float | None = None) -> dict | None:
|
||||||
|
"""Extract the per-share distribution table for ONE fund.
|
||||||
|
|
||||||
|
Anchors = 'distributions from / declared to / to shareholders' lines
|
||||||
|
immediately followed by 'net investment income' (the per-share
|
||||||
|
table, not the dollar-basis statement). Family annual reports
|
||||||
|
carry one table PER FUND (NexPoint's report has three) - when the
|
||||||
|
fund's current NAV is known, pick the table whose end-of-year NAV
|
||||||
|
is closest to it; otherwise the first.
|
||||||
|
"""
|
||||||
|
txt = re.sub(r"\s+", " ", txt)
|
||||||
|
tables: list[dict] = []
|
||||||
|
for m in re.finditer(
|
||||||
|
r"distributions (?:declared )?(?:from|to shareholders)",
|
||||||
|
txt, re.I):
|
||||||
|
if not re.search(r"net investment income",
|
||||||
|
txt[m.end(): m.end() + 300], re.I):
|
||||||
|
continue
|
||||||
|
t = _parse_table(txt[m.start(): m.start() + 3000])
|
||||||
|
if t:
|
||||||
|
tables.append(t)
|
||||||
|
if not tables:
|
||||||
|
return None
|
||||||
|
if nav_now:
|
||||||
|
def navdist(t: dict) -> float:
|
||||||
|
v = t["nav"][0] if t["nav"] and t["nav"][0] else None
|
||||||
|
return abs(v - nav_now) / nav_now if v else 99.0
|
||||||
|
tables.sort(key=navdist)
|
||||||
|
if navdist(tables[0]) > 0.15:
|
||||||
|
return None # no table matches this fund's NAV
|
||||||
|
return tables[0]
|
||||||
|
|
||||||
|
|
||||||
|
def roc_share(h: dict) -> float | None:
|
||||||
|
tot = sum(v or 0 for v in h["tot"])
|
||||||
|
if not tot:
|
||||||
|
return None
|
||||||
|
return sum(v or 0 for v in h["roc"]) / tot
|
||||||
|
|
||||||
|
|
||||||
|
def _nav_now(sym: str) -> float | None:
|
||||||
|
"""Latest raw NAV from the price file (to disambiguate family
|
||||||
|
reports that carry one per-share table per fund)."""
|
||||||
|
try:
|
||||||
|
import pandas as pd
|
||||||
|
p = Path.home() / "prog" / "fin" / "stocks" / f"{sym.lower()}-history.csv"
|
||||||
|
d = pd.read_csv(p, parse_dates=["Date"])
|
||||||
|
c = d["Close"].dropna()
|
||||||
|
return float(c.iloc[-1]) if len(c) else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def detect(sym: str, force: bool = False) -> dict | None:
|
||||||
|
"""Full pipeline for one fund: CIK -> latest N-CSR -> highlights."""
|
||||||
|
CACHE.mkdir(exist_ok=True)
|
||||||
|
cpath = CACHE / f"{sym.lower()}.json"
|
||||||
|
if cpath.exists() and not force:
|
||||||
|
return json.loads(cpath.read_text())
|
||||||
|
tc = edgar.ticker_to_company(sym)
|
||||||
|
if not tc:
|
||||||
|
return {"sym": sym, "error": "CIK not found"}
|
||||||
|
cik, name = tc
|
||||||
|
fil = edgar.cik_recent_filings(cik, "N-CSR", count=1)
|
||||||
|
if not fil:
|
||||||
|
return {"sym": sym, "error": "no N-CSR on file", "cik": cik}
|
||||||
|
f0 = fil[0]
|
||||||
|
txt = edgar.to_text(edgar.sec_get(
|
||||||
|
edgar.doc_url(cik, f0["accession"], f0["doc"]), timeout=120))
|
||||||
|
h = parse_highlights(txt, nav_now=_nav_now(sym))
|
||||||
|
rs = roc_share(h) if h else None
|
||||||
|
out = {
|
||||||
|
"sym": sym, "cik": cik, "trust": name,
|
||||||
|
"filing": f0["filed"],
|
||||||
|
"table": h,
|
||||||
|
"roc_share_5y": (round(rs, 3) if rs is not None else None),
|
||||||
|
"nav_change_5y": (round(h["nav"][0] / h["nav"][-1] - 1, 3)
|
||||||
|
if h and len(h["nav"]) >= 2
|
||||||
|
and h["nav"][0] and h["nav"][-1] else None),
|
||||||
|
}
|
||||||
|
cpath.write_text(json.dumps(out, indent=1))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _default_syms() -> list[str]:
|
||||||
|
dr = json.loads((HERE / "decompose_results.json").read_text())
|
||||||
|
xc = json.loads((HERE / "xcheck_report.json").read_text())
|
||||||
|
return sorted(set(s.upper() for s in dr)
|
||||||
|
| set(s.upper() for s in xc))
|
||||||
|
|
||||||
|
|
||||||
|
def _print(res: dict) -> None:
|
||||||
|
print(f" {'fund':<7} {'ROC 5y':>7} {'NAV 5y':>8} {'dist/yr (most recent first)':<44} filing")
|
||||||
|
for s in sorted(res, key=lambda x: -(res[x].get("roc_share_5y")
|
||||||
|
or -1)):
|
||||||
|
r = res[s]
|
||||||
|
if "error" in r:
|
||||||
|
print(f" {s:<7} ({r['error']})")
|
||||||
|
continue
|
||||||
|
h = r["table"]
|
||||||
|
if not h:
|
||||||
|
print(f" {s:<7} (no per-share table found)")
|
||||||
|
continue
|
||||||
|
yrs = []
|
||||||
|
for i in range(h["ncols"]):
|
||||||
|
t = h["tot"][i] if i < len(h["tot"]) else None
|
||||||
|
ro = h["roc"][i] if i < len(h["roc"]) else None
|
||||||
|
yrs.append(f"tot {t if t is not None else 0:.2f}"
|
||||||
|
+ (f"/roc {ro:.2f}" if ro else ""))
|
||||||
|
rc = r.get("roc_share_5y")
|
||||||
|
nv = r.get("nav_change_5y")
|
||||||
|
print(f" {s:<7} {(f'{rc*100:5.1f}%' if rc is not None else ' -'):>7} "
|
||||||
|
f"{(f'{nv*100:+6.1f}%' if nv is not None else ' -'):>8} "
|
||||||
|
f"{' '.join(yrs)[:44]:<44} {r['filing']}")
|
||||||
|
|
||||||
|
|
||||||
|
def run(syms: list[str] | None = None, force: bool = False) -> dict:
|
||||||
|
syms = syms or _default_syms()
|
||||||
|
res: dict = {}
|
||||||
|
for s in syms:
|
||||||
|
for attempt in range(3):
|
||||||
|
try:
|
||||||
|
r = detect(s, force=force)
|
||||||
|
break
|
||||||
|
except Exception as e: # SEC 503/429 throttling - back off
|
||||||
|
if attempt == 2:
|
||||||
|
r = {"sym": s, "error": f"{type(e).__name__}: {e}"}
|
||||||
|
else:
|
||||||
|
time.sleep(15 * (attempt + 1))
|
||||||
|
r = None
|
||||||
|
if r:
|
||||||
|
res[s.upper()] = r
|
||||||
|
time.sleep(0.5)
|
||||||
|
RESULTS.write_text(json.dumps(res, indent=1))
|
||||||
|
_print(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
args = [a for a in sys.argv[1:] if a != "--force"]
|
||||||
|
run(args or None, force="--force" in sys.argv)
|
||||||
Loading…
Reference in New Issue
Block a user