Answer to 'find other alpha-driven funds that complement the portfolio': - fundlab/search.py: complementarity screen - each fund's daily total returns vs the same 21 broad sleeve axes (BIC forward selection, |t|>2), full + 5y; alpha (intercept t), R2, rolling 6m alpha persistence, correlation vs the current qspnx/pmaix portfolio and the spy/agg/tlt benchmark mix. Verdict tiers: CANDIDATE (alpha + persistent + portfolio-corr<0.3) / semi-alpha / alpha-but-correlated / sleeve mix / weak. - fundlab/dbmine.py: the actual search universe - the local stocks DB already holds ~100 US open-end alternatives (AQR, PIMCO, JPM, Principal, Calamos, GMO, Franklin K2, ...). Name-pattern miner with share-class family dedupe (keeps the longest-history class). - fundlab/tickers.py + searchlist.py: external longlist resolution (chart-API name gate + EDGAR 497 cover tickers). Finding: the famous multi-strategy/macro names (Millennium, Balyasny, Two Sigma, Winton, Marshall Wace, Brevan Howard, AQR Event-Driven) are private/offshore or terminated - not US open-end accessible. Fidelity Multi-Asset Income (FMSDX) resolved and screens as weak alpha. - app Fund Lab: 'Alpha search - all screened funds, ranked' table (80 funds: 13 shortlist + 59 mined + 1 external). - results (ranked candidates, 5y alpha / t / portfolio-corr): wmnix Westwood Alt Income +3.8% t6.5 c0.09 | pyaix Payden ARB +3.0% t4.8 c0.13 | srdax Stone Ridge Div Alts +7.7% t4.2 c0.10 | padqx PGIM ARB +2.3% t2.4 c0.27 | bxmdx Blackstone Alt MS +3.5% t2.4 c0.30 | aqmix AQR Mngd Futures +8.0% t2.2 c0.21 | cmnix/gioix semi-alpha. Key insight: AQR MN / L/S-equity / Vanguard MN show strong alpha but corr 0.35-0.76 with the portfolio - it is already 50% market-neutral (qspnx), so more MN is not diversifying. - tests: 59/59 fundlab (resolver gates, query ladder, family dedupe, ticker regex), 32/32 app, 14/14 data
69 lines
3.8 KiB
Python
69 lines
3.8 KiB
Python
"""Curated longlist of candidate funds for the alpha search.
|
|
|
|
Universe rationale: we cannot meaningfully screen all ~10k registered
|
|
funds (most are small, illiquid or strategy-unstable and would not
|
|
deserve portfolio consideration anyway). So the UNIVERSE is curated by
|
|
reputation — large, liquid, long-tracked funds in the buckets where
|
|
idiosyncratic alpha lives (market-neutral/quant, multi-strategy, global
|
|
macro, dynamic TA, dynamic credit/convertibles) — and the SELECTION is
|
|
data-driven (fundlab/search.py). Tickers are resolved from the fund NAME
|
|
via Yahoo search with a precision gate; a candidate that cannot be
|
|
resolved cleanly is dropped, never guessed.
|
|
|
|
The 13-fund shortlist already in funds.json is screened with the same
|
|
code so the ranking is consistent (qspnx/pmaix are the user's current
|
|
holdings and act as controls).
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
# (fund name as searched, strategy bucket, ticker guess or None)
|
|
LONGLIST: list[tuple[str, str, str | None]] = [
|
|
# --- market-neutral / quant / pure alpha / event-driven ------------
|
|
("AQR Diversified Event-Driven Fund", "event_driven", None),
|
|
("Bridgewater Pure Alpha II Fund", "pure_alpha", None),
|
|
("Winton Global Quantitative Fund", "cta_quant", None),
|
|
("Two Sigma Dynamic Strategy Fund", "systematic", None),
|
|
("Brevan Howard Dymon Asia Fund", "macro_relative_value", None),
|
|
("Marshall Wace Global Opportunities Fund", "macro_relative_value",
|
|
None),
|
|
# --- multi-strategy -------------------------------------------------
|
|
("ExodusPoint Diversified Fund", "multi_strategy", None),
|
|
("Verition Dynamic Risk Fund", "multi_strategy", None),
|
|
("Millennium Focus Fund", "multi_strategy", None),
|
|
("Balyasny Absolute Return Multi-Strategy Fund", "multi_strategy", None),
|
|
("Schonfeld Strategic Opportunities Fund", "multi_strategy", None),
|
|
# --- global macro / risk parity ------------------------------------
|
|
# (T. Rowe Price New Global Opportunity: fund terminated - no data)
|
|
("Bridgewater All Weather Fund", "risk_parity", None),
|
|
("Oak Hill Tactical Allocation Fund", "tactical_allocation", None),
|
|
# --- multi-asset / dynamic TA open-ends (the accessible alpha pool) -
|
|
("Fidelity Multi-Asset Income Fund", "tactical_allocation", None),
|
|
("Janus Henderson Global Dynamic Dividend Fund", "dynamic_equity", None),
|
|
("Wellington Dynamic Global Diversified Fund", "tactical_allocation", None),
|
|
("Lord Abbett Global Opportunities Fund", "tactical_allocation", None),
|
|
("BlackRock Multi-Asset Income Fund", "tactical_allocation", None),
|
|
("PIMCO Income Strategy Fund", "tactical_allocation", None),
|
|
("JPMorgan Diversified Return Fund", "tactical_allocation", None),
|
|
("Invesco Diversified Equity and Income Fund", "tactical_allocation", None),
|
|
("T. Rowe Price Global Allocation Fund", "tactical_allocation", None),
|
|
("Morgan Stanley Global Multi Asset Fund", "tactical_allocation", None),
|
|
("Fidelity Diversified Multi-Asset Fund", "tactical_allocation", None),
|
|
# --- controls: credit (expected to classify as sleeve mix)
|
|
# (Calamos Dynamic Convertible CCD is a CEF - excluded)
|
|
("PIMCO Dynamic Income Fund", "dynamic_credit", None),
|
|
]
|
|
|
|
# the 13 unique shortlist funds (share classes resolved once)
|
|
SHORTLIST = ["atesx", "atrfx", "cvsix", "jlpsx", "pmaix", "pmorx", "qspnx",
|
|
"svarx", "cosix", "mbxix", "eagmx", "lcorx", "lamhx"]
|
|
|
|
# broad sleeve set used by the screen (same 21 axes for every fund -
|
|
# nothing is tuned to a specific fund, so selection is comparable)
|
|
BROAD_SLEEVES = ["qqq", "ivv", "iwm", "vea", "efa", "vwo", "vnq", "bil",
|
|
"shv", "ief", "tlt", "vblix", "agg", "vweax", "vmbix",
|
|
"finux", "djp", "gsg", "gld", "fxe", "fxy"]
|
|
|
|
# the user's current portfolio + benchmark mix (settings.json)
|
|
PORTFOLIO = {"qspnx": 0.5, "pmaix": 0.5}
|
|
BENCHMARKS = {"spy": 1 / 3, "agg": 1 / 3, "tlt": 1 / 3}
|