|
Download README.md from s-ttp/vitals: direct link, hf CLI and curl.
- Browser
- Download file 28.2 kB
-
https://huggingface.co/spaces/s-ttp/vitals/resolve/main/README.md
- Command line
-
hf download hf://spaces/s-ttp/vitals/README.md
-
curl -L -o README.md https://huggingface.co/spaces/s-ttp/vitals/resolve/main/README.md
28.2 kB
| title: Vitals | |
| emoji: π©Ί | |
| colorFrom: blue | |
| colorTo: gray | |
| sdk: docker | |
| app_port: 7860 | |
| pinned: false | |
| # Vitals β *Your portfolio's AI health check* | |
| Five AI analyst personas examine your stock portfolio in private; you see the verdict and | |
| the treatment plan. Key in your holdings (ticker or company name + | |
| entry price, quantity optional), and get: | |
| - **Per-holding recommendations** β Keep / Buy More / Trim / Sell, decided by deterministic | |
| rules from a 5-persona AI conviction panel (Value Β· Growth Β· Macro Β· Technical Β· Risk). | |
| - **Intrinsic value** β a Damodaran-style 2-stage DCF per holding (an LLM sets lifecycle | |
| assumptions, auditable code does the math), with value-trap guards. | |
| - **Portfolio-level review** β sector diversification vs healthy bands, concentration | |
| (effective N), correlation clusters, beta, Sharpe/drawdown, strategy coherence, and a | |
| deterministic OK/WATCH/ALERT risk scorecard that renders even if every LLM call fails. | |
| - **Alternatives** β gap-filling equity candidates cued from your existing portfolio, merged | |
| from two signals (MarketFM quant factor rank + Slipstream smart-money 13F/insider | |
| accumulation), plus an optional context intake that unlocks a **recommended target portfolio**. | |
| - **Verdict ledger & standing watch** β save a portfolio under an anonymous code; every | |
| verdict is logged with its price and later scored per action vs the S&P 500 (the committee | |
| keeps a track record it must answer to). A background sweep plus on-demand checks raise | |
| alerts on price shocks, 200-day trend breaks, stale verdicts, and cited adverse news events. | |
| Case IDs are remembered per device and shareable via `?p=<code>` links. | |
| Ledger persists to a private HF dataset (`VITALS_DATA_REPO`, default `s-ttp/vitals-data`). | |
| - A downloadable, typeset PDF report with the full committee detail, plus a **JSON audit | |
| trail** for every run (inputs, data snapshot with sources and as-of dates, every lens | |
| output, model and prompt versions, decisions) so any verdict can be reproduced. | |
| ### Institutional-grade layer | |
| - **Data integrity** β Alpha Vantage premium is the primary market-data source (adjusted | |
| history, quotes, splits, dividends and earnings calendars, ETF constituents, FX) and Sharadar | |
| SF1 (licensed, point-in-time) the primary fundamentals source for US equities; scraped feeds | |
| are fallback only (indices and exchanges Alpha Vantage does not map); every field passes a plausibility | |
| gate (units and feed errors are nulled and flagged, never fed to a model); every number | |
| carries an as-of stamp; London pence/pound and D/E/yield unit traps are normalized. | |
| - **Valuation rigor** β bear/base/bull intrinsic ranges instead of a point estimate, a | |
| **reverse DCF** ("today's price assumes X%/yr growth"), sector-appropriate models | |
| (FFO-based DCF for REITs, justified P/B vs ROE for banks and insurers), and peer | |
| percentiles vs the US universe. | |
| - **Risk analytics** β benchmark chosen by domicile (NIFTY, FTSE, Nikkeiβ¦ not SPY for | |
| everyone) with a base-currency view; historical stress replays (COVID, 2022 rate shock, | |
| Q4-2018, 2025 tariff shock) of today's weights; 1-day/1-month VaR and expected shortfall; | |
| tracking error; currency exposure; real **ETF look-through** from disclosed constituents; | |
| style-factor tilts; days-to-liquidate per position. | |
| - **Reproducibility** β each analyst lens is sampled `LENS_SAMPLES` times (default 2) and | |
| medianed so a verdict doesn't flip on re-run; strict schema coercion on every LLM output; | |
| prompt and methodology versions stamped on every verdict; per-lens calibration (hit rate | |
| vs benchmark) accumulates in the ledger. | |
| - **Portfolio-aware recommendations** β alternatives are not a stock screen: the engine | |
| first diagnoses what *this* book lacks (sector gaps, overweight, effective positions, | |
| crash loss vs stated tolerance, income need, valuation tilt), then scores every | |
| live-verified candidate on its **measured marginal improvement** β correlation to your | |
| holdings, effective-N, beta, worst crash replay, yield, DCF gap β and only fit-eligible | |
| names reach the CIO and the switch engine. Each pick states the problem it fixes with the | |
| number ("moves opposite your holdings (correlation β0.40) Β· cuts the 2022 replay β53%ββ49%"). | |
| Deterministic guards a quant rank never applies (peak-cycle earnings in a cyclical business, | |
| an extended price, informed money leaving) surface as cautions and cost fit; then the same | |
| five analyst lenses **screen every shortlisted candidate as a prospective addition** β a | |
| name the committee would not add (below 52/100) is never recommended or used as a switch | |
| destination. | |
| - **Portfolio construction** β risk decomposition (marginal contribution to risk per | |
| holding, Ledoit-Wolf shrinkage covariance), adds chosen **jointly** (each conditional on the | |
| one before it, by variance reduction plus need-fit β two redundant names never both win), | |
| **constrained sizing** (SLSQP under name/sector caps, a turnover budget and a tax penalty) | |
| in three scenarios β full rebalance, tax-light, new-cash-only β and the **entire risk | |
| block re-run on the recommended book** (stress replays, VaR, effective positions, health) | |
| side by side with today. Instruments (sector ETFs, treasuries, cash, dividend and | |
| international ETFs) compete with single stocks as fixes. Guards: earnings-date and | |
| ex-dividend checks, client exclusions and a max-positions constraint; every candidate | |
| considered but not recommended is listed with the reason. | |
| - **Recommendation completeness** β every Trim/Sell carries its tax cost (gain and, with an | |
| optional purchase date, short- vs long-term treatment); orders carry estimated | |
| transaction cost and liquidity notes; investor context now includes loss tolerance and | |
| near-term cash need, checked against the stress replays; income/conservative investors get | |
| profile-fitting candidates; non-US books get live-verified local-market ideas. | |
| - **Engineering QA** β `pytest` regression suite (valuation math, decision validator, | |
| schema coercion, sanity gates, a mocked golden end-to-end run) and per-run diagnostics | |
| (duration, LLM calls/failures, model versions). | |
| **Educational tool β not investment advice.** No orders are ever placed; output is analysis only. | |
| ## Token cost strategy | |
| Every run reports tokens and estimated cost by model and by task in the diagnostics (and a | |
| "Model usage this run" line in the report). The model plan, chosen so that judgment stays on | |
| the strongest model and mechanics do not pay for reasoning they do not need: | |
| | Task | Model | Reasoning | Why | | |
| |---|---|---|---| | |
| | CIO synthesis, pre-mortem, alternatives | Claude Opus 5 | adaptive (default) | the one judgment call that shapes the verdict | | |
| | Portfolio strategist | Claude Sonnet 5 | adaptive | structured read of computed metrics | | |
| | Value / Risk lenses | Claude Sonnet 5 | medium effort | rubric scoring against a fact sheet; shared fact-sheet block is prompt-cached | | |
| | Growth / Technical lenses | GPT-5.6 Sol | provider default | model diversity across the committee | | |
| | Macro lens | Gemini 3.7 Flash | provider default | cheapest lens, macro positioning only | | |
| | DCF assumptions, plan narrative | Claude Sonnet 5 | low effort | numeric mapping with clip guards; prose | | |
| | Per-holding rationales | GPT-5.6 Luna | thinking disabled | reason-only sentences with a leading-action guard; split out of the CIO so its response is a third of its former size | | |
| | Investment memos | GPT-5.6 Luna | thinking disabled | A/B on real payloads: memos as good as Sonnet's at ~1/13 the cost, 10 s vs 17 s | | |
| | Ticker resolution, listing check, event scan | Gemini 3.1 Flash Lite β Haiku | thinking disabled | 10/10 on renamed tickers at 1.0 s a call for ~nothing; Haiku via the cascade | | |
| | File import | Claude Sonnet 5 | thinking disabled | once per import; plain tables never reach a model | | |
| Tested and rejected: Luna as a lens (growth-lens scores drifted 7 points from GPT-5.6 Sol | |
| against 2.4 points of Sol's own repeat noise β enough to flip Keep/Buy More bands) and Luna for | |
| DCF assumptions (lifecycle classifications diverged from Sonnet's). Gemini 3.7 Flash as a value | |
| lens tracked Sonnet at 0.93 correlation but is kept on the macro lens only, so the committee stays | |
| diverse. | |
| Claude 5 models think by default; on a 24-name book that was 19k thinking tokens on Sonnet | |
| alone. Effort levels and disabled thinking remove most of it without touching the tasks where | |
| reasoning matters. | |
| ## Speed on US books | |
| - **Sharadar price layer** (`src/sharadar_prices.py`): US prices, 52-week range, moving | |
| averages, volume, beta, market cap and multiples come from SEP / SFP / DAILY / TICKERS / | |
| ACTIONS in batched calls with no meaningful rate limit; Alpha Vantage is kept for what Sharadar | |
| lacks (earnings surprises and estimate revisions, the analyst-target overview, news sentiment, | |
| ETF constituents, FX) and remains primary for every non-US market. Earnings dates come from one | |
| whole-market calendar call a day. | |
| - **Pre-warming**: each row resolved at import kicks off the deep fundamentals, eight-year | |
| history, street view, news and events in the background, so the analysis finds them cached. | |
| - **Batched import verification**: one server round trip resolves every imported row in | |
| parallel; the ten-year range and split checks run only when the entry price implies an | |
| extreme P/L. | |
| - **Split CIO**: investment memos are written by a parallel writer on the standard model in | |
| chunks, so the CIO's own response is a quarter of its former size. | |
| - **Time budget**: the watchdog scales with book size; past 65% of the budget the candidate | |
| committee screen is skipped and past 85% the plan narrative, with the diagnostics saying so. | |
| - **Plain tables never wait for a model**: a CSV/XLSX with a symbol column and an | |
| average-cost column is read directly; the AI extractor handles transaction exports, PDFs and | |
| odd layouts on the standard tier. A pre-split entry price is never rewritten silently: the row | |
| asks which is right β adjust price and quantity, adjust price only (quantity already | |
| post-split), or keep as entered β and the analysis refuses to run until the question is | |
| answered; the results row shows what was chosen. A 10:1 split can hide behind an | |
| ordinary-looking P/L, and only the investor knows whether their share count is pre- or post-split. | |
| - **Pipeline parallelism**: the portfolio strategist and the whole candidate pipeline (pool, | |
| live verification, fit, screens, committee) run alongside the five-lens panel; books of 30+ | |
| names use one sample per lens. | |
| - **US-only fast path**: a book with no exchange suffixes skips the European panel, ADR bridge, | |
| local-idea generation and multi-venue logic deterministically. | |
| - The European panel rebuild yields to running analyses and is throttled to a fraction of the | |
| Alpha Vantage budget. | |
| ## Markets covered | |
| | Market | Yahoo suffix | Prices / history / FX | Fundamentals | | |
| |---|---|---|---| | |
| | US (NYSE, NASDAQ) | β | Alpha Vantage | Sharadar (point-in-time) | | |
| | UK Β· Germany (Xetra, Frankfurt) Β· France Β· Netherlands Β· Portugal Β· Belgium Β· Canada (TSX, TSX-V) | `.L .DE .F .PA .AS .LS .BR .TO .V` | Alpha Vantage | Alpha Vantage where available, else yfinance (sanitized) | | |
| | India (NSE, BSE) | `.NS .BO` | Alpha Vantage **BSE line** (NSE names are dual-listed at near-identical prices; the NSE symbol is kept) | **Sharadar via the company's NYSE/NASDAQ ADR** where one exists (HDFC Bank β HDB, Infosys β INFY: ratio fields are the underlying company's, EPS/BVPS derived currency-consistently from local price Γ P/E, P/B); else yfinance (sanitized) | | |
| | China (Shanghai, Shenzhen) | `.SS .SZ` | Alpha Vantage | Sharadar via ADR where one exists (Alibaba β BABA), else yfinance (sanitized) | | |
| | Switzerland, Italy, Nordics, Japan, Korea, Hong Kong, Australia, Brazil and other Yahoo-suffixed exchanges | e.g. `.SW .MI .CO .T .KS .HK .AX .SA` | yfinance (fallback) | Sharadar via ADR where one exists (Vale β VALE, TSMC β TSM, Shell, SAP, Novartisβ¦), else yfinance (sanitized) | | |
| **Several purchases of one stock** may be entered as separate rows (same ticker, each lot's price, | |
| quantity and date); they are averaged with the average-cost method (quantity-weighted cost, sales as | |
| negative quantities reduce the remaining position at that cost β the Section 104 pool logic), the | |
| earliest lot dates the position, and US tax notes name the lots still short-term. Without quantities | |
| the entry prices are averaged plainly and the row asks for quantities. | |
| Inputs may be tickers, company names (resolved deterministically through Alpha Vantage Symbol | |
| Search β preferring the home-market listing over a US OTC ADR, and mapping India to the NSE | |
| line β before any LLM is consulted), Bloomberg-style codes ("VOD LN" β resolved with a listing | |
| check) or **ISINs** (OpenFIGI β primary listing). Renamed or demerged tickers are re-searched by | |
| their base symbol ("Tata Motors" β TMCV.NS / TMPV.NS). Benchmarks follow the book's domicile (S&P | |
| 500, FTSE 100, STOXX Europe 600, DAX, NIFTY 50, SSE Compositeβ¦); European books get UCITS | |
| instruments, Indian books get NSE-listed BeES ETFs, and local-market ideas are verified live on | |
| the same exchanges. Tax notes apply US short/long-term rules only to US listings. | |
| ### US books β practitioner layer (`src/fundamentals.py`) | |
| Plain US listings get a deep block computed deterministically from Sharadar history and Alpha | |
| Vantage estimates, so the committee argues from numbers rather than adjectives: | |
| - **Forensic quality** β Piotroski F-score, Altman Z (masked for financials and REITs), Beneish | |
| M-score, Sloan accruals, cash conversion (FCF / net income). | |
| - **Buffett / Greenblatt / Lynch economics** β owner earnings and owner-earnings yield, FCF yield, | |
| ROIC and five-year incremental ROIC, EBIT/EV earnings yield, Graham number, PEG on three-year EPS | |
| growth, and the Lynch category (fast grower, stalwart, slow grower, cyclical, turnaround, asset | |
| play) that decides which metrics matter and which sell triggers apply. | |
| - **Capital allocation** β buyback yield vs dilution (1y and 5y share count), stock-based | |
| compensation as a share of revenue and FCF, FCF payout and the dividend-raise streak. | |
| - **Valuation vs the company's own ten-year history** β where today's P/E, EV/EBITDA, P/S and | |
| P/B sit in the daily distribution (Sharadar DAILY), with the median. | |
| - **Earnings momentum** β beat count and average surprise over eight quarters, fiscal-year EPS | |
| estimate revision over 90 days, up/down revision counts over 30 days. | |
| - **Ownership** β open-market insider buys and sells over six months (SF2, cash transactions | |
| only) and 13F holder / share changes (SF3A, stale or coverage-jump snapshots suppressed). | |
| - **Sell discipline** β per-holding thesis-break triggers from the same numbers, forensic flags | |
| that also screen alternatives (a factor rank never outranks a distress or manipulation flag). | |
| Recommendation discipline for every book with a usable DCF range: | |
| - **Payoff asymmetry** β bear / base / bull outcomes with probabilities tilted by committee | |
| conviction give an expected 12β18 month return and an upside:downside ratio; a "Buy More" | |
| must clear 2:1 and +8% or it is downgraded to Keep with the reason shown. | |
| - **Sizing guardrail** β half-Kelly from expected return and realised volatility, capped at the | |
| name cap, shown against the current weight. | |
| - **Investment memo per holding** (thesis, the debate, what would change our mind), a | |
| **pre-mortem** for the largest position, an explicit **"do nothing"** verdict when nothing | |
| clears the hurdle, **tax-loss harvesting pairs** (β₯10% loss, β₯0.7-correlated non-identical | |
| substitute; a broad index fund swaps only into a β₯0.9-correlated broad fund on a different | |
| index, e.g. VOO β VTI / SCHX, never a sector fund; 31-day wash-sale note) and wash-sale warnings on loss-making switches. | |
| - **Health score = six pillars, shown under the gauge** β Prospects 25% (committee conviction, | |
| DCF-implied payoff where trusted, quant 12-month excess, growth durability, estimate momentum; | |
| growth-stage names are never judged on a mature-company DCF gap), Concentration 20% (name vs cap, | |
| top three, sector vs band, largest active bet), Diversification 15% (effective positions, | |
| correlated pairs, ballast for cautious profiles), Quality 15% (weight in forensic-flagged | |
| names, Piotroski), Drawdown 15% (half efficiency vs structural beta, half absolute fall vs the | |
| index; 25% for conservative, 8% for aggressive profiles), Valuation 10% (DCF gap where the | |
| model applies, blended toward neutral for the share of the book it does not cover, P/E vs own | |
| history). Active sector bets fall back to a static S&P 500 mix when the live profile is | |
| unavailable, so the charge cannot appear and vanish between runs. One-year performance is a Β±3 tie-breaker only; a breached | |
| loss tolerance costs 8; the weakest pillar caps the total (one broken pillar cannot be | |
| outvoted by strong prospects). | |
| - **A stock-picker's plan never holds an index fund** β for a book of 12+ single names with no | |
| cautious profile or stated loss tolerance, no ETF enters the candidate pool at all: sector | |
| gaps, ballast and diversification are expressed through single names. A conservative profile, | |
| a short horizon or a loss tolerance brings defensive instruments back. | |
| - **Stock-picker books get stock ideas** β a book of 12+ single names is treated as a | |
| stock-picker's: the CIO sees single names first, at least two alternatives are single stocks | |
| and at most one is an instrument; smaller books or de-risking needs still get instruments. | |
| - **Sizing discipline** β a new single name enters at a starter size (β1.5Γ the typical | |
| position, at least 3%, never past the cap); an existing name may at most double or gain three | |
| points of the book; instruments are bounded in a large single-name book; the target keeps at | |
| least 75% of today's market beta unless the investor asked to de-risk (a growth book is never | |
| silently converted into utilities); risk aversion scales with the stated profile; a partial | |
| sale of a position below the trading band (min(2%, max(1%, 0.5/n)) β 1.06% in a 47-name book) is | |
| replaced by a clean exit or a hold; a size-driven Trim on a backed winner lands between 80% of the | |
| cap and the cap (META 24% β 12β15%), never at 40% of the position; of a correlated pair the plan | |
| adds to one name only; a sector ETF is sized like a | |
| sector bet (never past the name cap or 15%), and every new name the plan buys is explained in | |
| the alternatives list; a conservative / income book never adds to a name with beta above 1.15 | |
| and needs a flagged idea (peak-cycle, extended, informed money leaving) at β₯66/100. | |
| - **The manager's desk** β capital follows conviction: fresh money goes only to Buy More verdicts | |
| and new ideas (a plain Keep is never topped up; a backed Keep below the trading band is rounded up | |
| to a real position; an add is lifted to at most 6% of a broad book, 10% of a small one); proceeds | |
| the committee's ideas cannot absorb stay as **unallocated cash**, | |
| presented as dry powder for a **watchlist** of screened-out names with explicit entry conditions | |
| (reclaim the 200-day, pull back to it, wait for revisions to turn). Keeps with weak backing and a | |
| negative expected payoff carry a **watch** tag and are the first source of funds; a sub-band | |
| position nobody backs is exited rather than held as dead money. Every sell carries a **tax plan** | |
| (highest-cost lots first, hold a lot that turns long-term within 90 days, split a large long-term | |
| gain across two tax years); the plan shows **where the risk sits after the trade**, a | |
| probability-weighted **valuation risk/reward** (bear/base/bull convergence) for today's and the recommended book, the next | |
| 90 days of **catalysts** (earnings, ex-dividend), and per-holding **tripwires** with the current | |
| number and the date they are next tested. Loss-making growth names are valued on a stated | |
| rule-of-40 EV/Sales yardstick instead of a blank. Triage dispositions always agree with the trade list. | |
| - **Sector gaps are sourced, not just named** β a sector the book lacks is searched six names | |
| deep in the quant panel (two for sectors already owned), the top 30 are verified live, the | |
| committee screens a second batch whenever a missing sector has no strong candidate, and the | |
| shortlist leads with the best strong name from each missing sector so the optimizer can actually | |
| move the sector mix. A book with zero Industrials, Materials, Defensive and Utilities once received | |
| one Technology idea because its eight gap candidates all failed a screen and nothing replaced them. | |
| - **Valuation guardrails** β the justified-P/B model is used only for banks, insurers and asset | |
| managers with book-value economics (ROE β€ 35%, book β₯ 15% of price, no leverage-inflated ROE); | |
| payment networks and fintechs go on the earnings DCF; when forward EPS is β₯1.4Γ depressed trailing | |
| EPS the DCF anchors on their average and says so; reinvestment is capped at 80% of earnings so a | |
| fast grower on a modest accounting ROIC is not priced as value-destroying; an Altman-Z distress flag | |
| disqualifies a candidate outright. The CIO synthesis retries on the standard tier after two | |
| unusable top-tier replies, JSON parsing repairs control characters and trailing commas, and every | |
| unusable model reply is saved under data/cache for diagnosis. | |
| - **Loss tolerance as a constraint** β when the investor states a loss tolerance, every crash | |
| replay becomes a linear constraint in the optimizer, so the proposed book must stay inside the | |
| tolerance in each historical window; the scenario table says whether it was reachable. | |
| - **A model reply is validated, not trusted** β the CIO's answer must carry its verdict (a | |
| truncated reply once parsed as an inner fragment and slipped through as an empty verdict, so | |
| the report fell back to the scorecard); the retry lowers reasoning effort so the budget goes to | |
| the answer, and any fallback is printed in the report's diagnostics note. | |
| - **Winners are not sold early** β the validator's two-lens dissent tilt (two analysts at 35 or | |
| below overriding a momentum-led median) applies only to cyclicals, judged by sector or by | |
| industry (semiconductors, memory, autos, steel, chemicals, oil and gas, mining, shippingβ¦); a | |
| secular compounder keeps its Keep or Buy More while its growth and trend lenses back it. Size | |
| alone still trims any name past 1.25Γ its cap unless the committee is strongly behind it. | |
| - **Orders agree with verdicts** β a Keep the committee only lukewarmly backs (conviction under | |
| 55) or that carries a forensic flag gets no adds; a backed Keep outside the overweight sector is | |
| never sold below 80% of its weight to fund other buys; only a new position can fill a sector gap | |
| and the plan narrative is told so. | |
| - **Plan coherence** β switch destinations follow the names the order plan actually buys; a | |
| "Buy More" is never issued inside the diagnosed overweight sector; bond and cash funds are | |
| ballast and are never ejected by the equity lenses in a conservative or short-horizon book; | |
| US-listed ADRs derive per-share values from the ADR price and Sharadar ratios (ordinary-share | |
| EPS once made TSM look 4Γ undervalued). | |
| - **Benchmark-relative risk** for US books β measured factor betas (momentum, quality, value, | |
| low-vol, size ETFs), upside / downside capture, Sortino, Calmar, active sector bets vs the | |
| S&P 500. | |
| ### UK / European books β institutional layer | |
| - **European factor panel** (`src/europanel.py`): the MarketFM equivalent for Europe, built on the | |
| Space from Alpha Vantage β universe = the top ~300 European names by index weight (VGK, SPEU, | |
| DBEU, FEZ constituents) mapped to their home listings (LSE, Xetra, Paris, Amsterdam, Lisbon, | |
| Brussels; Xetra lines for markets Alpha Vantage does not carry), per-name valuation / quality from | |
| the same holding fetch the analysis uses (Alpha Vantage price, Sharadar via ADR, yfinance fill, | |
| sanity gates) and momentum / volatility from Alpha Vantage adjusted daily history, | |
| cross-sectional percentile ranks and a style-balanced composite (value, quality, momentum, low-vol). | |
| Refreshed daily in the background; `data/europanel.json` ships with the last build. European-heavy | |
| books (β₯50% weight in European listings) draw quant-ranked candidates from it first, and European | |
| holdings get **peer percentiles against the European universe**, not the US one. | |
| - **European sector UCITS instruments**: iShares STOXX Europe 600 sector ETFs (Xetra) and IPRP | |
| (European property) fill sector gaps for European books, alongside the broad / gilt / gold / | |
| dividend / min-vol UCITS set. | |
| - **Cost & tax realism**: the order plan adds UK stamp duty (0.5%), Irish stamp duty, French, | |
| Italian and Spanish FTTs and HK stamp duty on home-market share purchases (ETFs exempt); an | |
| **account wrapper** input (taxable / ISA / SIPP / IRA) switches capital-gains notes off inside | |
| tax-sheltered wrappers; foreign dividend payers carry a withholding-tax note. | |
| ## Space secrets | |
| | Secret | Required | Purpose | | |
| |---|---|---| | |
| | `ANTHROPIC_API_KEY` | recommended | Claude β Value & Risk analysts, DCF assumptions, strategist, CIO. | | |
| | `OPENAI_API_KEY` | recommended | GPT β Growth & Technical analysts. Different models give independent opinions; with any one key the others cascade onto it. | | |
| | `GEMINI_API_KEY` | recommended | Gemini β Macro analyst + nameβticker fallback. | | |
| | `ALPHAVANTAGE_API_KEY` | recommended | Premium market data (primary): history, quotes, splits, calendars, ETF constituents, FX, news. `AV_CALLS_PER_MIN` (default 75) sets the limiter. | | |
| | `HF_TOKEN` | optional | Read the private MarketFM panel dataset (quant ranks + alternatives pool). | | |
| | `NASDAQ_DATA_LINK_API_KEY` | recommended | Sharadar SF1 β licensed, point-in-time US fundamentals as the primary source (and REIT FFO inputs). | | |
| With no LLM key at all the app still runs and shows the quantitative summary. | |
| Models are tier-routed by task demand (all env-overridable): synthesis & valuation | |
| judgment on Claude Opus 5 (`LLM_MODEL_TOP`), Value/Risk lenses + import on Claude | |
| Sonnet 5 (`LLM_MODEL`), Growth/Technical lenses on GPT-5.6 Sol (`OPENAI_MODEL`), | |
| Macro on Gemini 3.7 Flash (`GEMINI_MODEL`), utilities on Claude Haiku 4.5 | |
| (`LLM_MODEL_FAST`). Other config: `RUNS_PER_DAY` (default 25), `MAX_HOLDINGS` | |
| (default 25), `CACHE_TTL_HOURS` (default 6). | |
| Other env vars: `LENS_SAMPLES` (default 2), `MAX_CONCURRENT_RUNS` (default 2). | |
| ## Run locally | |
| ``` | |
| pip install -r requirements.txt | |
| uvicorn app:app --port 7860 | |
| ``` | |
| ## Policy validation | |
| `scripts/backtest_fit_policy.py` walks forward quarterly over ~5 years on three seed books and | |
| compares the greedy variance-reduction add rule against random adds, momentum adds and no adds | |
| (12-month holds); results land in `data/policy_backtest.json`. | |
| Latest run (2021-Q1 β 2025-Q2, 18 rebalance dates per seed): the greedy rule delivered the | |
| **lowest volatility and shallowest max drawdown in every seed book** (tech-concentrated: 31.6% | |
| vol / β24.0% DD vs 33.7% / β25.8% random, 35.4% / β26.7% momentum, 41.4% / β31.1% with no | |
| adds) and the best Sharpe on the tech book (1.34); on the income and mid-cap books momentum | |
| adds earned more return. Read it plainly: the construction rule reliably buys diversification, | |
| not alpha β return is the job of the fit scoring and the analyst committee layered on top. | |
| ## Tests | |
| ``` | |
| pip install -r requirements-dev.txt | |
| pytest | |
| ``` | |
| ## Architecture | |
| FastAPI + SSE progress stream + vanilla JS frontend (no build step). The analysis pipeline | |
| (`src/orchestrator.py`) runs in-process: resolve holdings β fetch market data (yfinance | |
| global, Alpha Vantage enrichment) β deterministic portfolio metrics β Damodaran DCF β | |
| batched 5-persona conviction panel β portfolio strategist β CIO synthesis. LLM outputs are | |
| gated by deterministic validators (actions come from conviction-median rules, never from | |
| free-form LLM text); every LLM stage fails open. | |