Skip to content

Fix sleeper DCF: cap growth rates to enforce bear ≤ base ≤ bull ordering - #6

Open
rmalhotra25 wants to merge 10 commits into
masterfrom
fix-sleeper-dcf
Open

Fix sleeper DCF: cap growth rates to enforce bear ≤ base ≤ bull ordering#6
rmalhotra25 wants to merge 10 commits into
masterfrom
fix-sleeper-dcf

Conversation

@rmalhotra25

Copy link
Copy Markdown
Owner

For high-growth stocks appearing in sleepers (e.g. NVDA with 65.5% rev growth), the base case used uncapped rev_growth while the bull case was capped at 30%. This inverted the ordering (base $15T > bull $5.8T for NVDA).

Fix: cap base at min(rev_growth0.80, 20%) and bear at min(rev_growth0.30, base), enforcing bear ≤ base ≤ bull. Also includes the newline-fix and FCF net margin proxy commits already in master.


Generated by Claude Code

claude added 10 commits May 10, 2026 22:33
…eleration

- _fetch_fundamentals: add 8 new fields from same Finnhub call at no extra
  API cost: 6mo/3mo price returns, FCF margin, ROIC, quarterly rev growth
- _add_derived_signals: compute rev_accel (quarterly vs TTM growth speed)
  and growth_efficiency (rev growth / P/S, PEG-style) per stock
- _pct_ranks: percentile-normalize each metric across the scanned universe
  so scoring reflects relative rank, not arbitrary fixed thresholds
- _build_compounder_scores / _build_sleeper_scores: weighted multi-factor
  models replacing the old point-ladder system; momentum and acceleration
  are now first-class signals alongside margins and valuation
- _fmt_candidates: richer context sent to Claude (FCF, momentum, ROIC,
  [↑ACCEL]/[↓DECEL] tags) so thesis quality improves with the data
- PickCard: new chips for FCF Margin, ROIC, 6mo Return (green/red),
  Acceleration (green/red arrow); MetricChip supports accent color

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
finnhub_client.py:
- get_insider_sentiment(): fetches last 90 days of direct (non-derivative)
  insider transactions; requires 2+ distinct insiders for a buy/sell signal
  to filter out noise from single-exec routine option exercises

discovery_engine.py (Phase 3 enrichment):
- For each finalist (top 35 per category, rate-limited) fetch:
  - Finnhub insider sentiment: signal + buyer/seller counts stored on dict
  - Polygon short interest: days_to_cover + short_vol_pct (optional, no
    Finnhub rate limit, silently skipped if Polygon unavailable)
- _fmt_candidates: passes InsiderBuying/InsiderSelling and ShortDTC context
  to Claude so theses can reference these signals
- _enrich: exposes insider_signal, insiders_buying, insiders_selling,
  days_to_cover, short_vol_pct to the frontend

DiscoveryTab.jsx:
- insiderBuyBadge (green) and insiderSellBadge (orange) styles added
- PickCard: shows "Insiders Buying (N)" / "Insiders Selling (N)" badge
  when signal is triggered; Short DTC chip highlights squeeze potential
  (green accent when DTC > 10 days)

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
discovery_engine.py:
- _fmt_candidates: d['pe'] / d['ps'] → d.get('pe') / d.get('ps') so the
  expression is safe even if the key is absent (guard on left side of
  conditional already prevents reaching the round() call, but using .get()
  is defensive and consistent with every other field access in this file)
- _enrich: same fix for the two round() calls on pe and ps

DiscoveryTab.jsx:
- insiders_buying / insiders_selling comparisons use ?? 0 so that old
  cached scan results (pre-dating these fields) degrade gracefully instead
  of comparing undefined >= 2

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
…o Discovery picks

- New `_dcf_scenarios()`: 10-year FCF DCF with bull/base/bear scenarios using
  PS-derived revenue and gross-margin FCF floor for pre-profit compounders.
  Compounders use 12% discount rate; sleepers use 10%.
- DCF recommendation (Strong Buy/Buy/Hold/Pass) driven by base-case upside vs
  current market cap.
- Updated Claude prompt to request `bull_case`, `bear_case`, and `long_term_rec`
  fields alongside existing thesis/catalyst/risk.
- Frontend: new `DcfSection` component shows Bear/Base/Bull intrinsic values
  with % upside, DCF recommendation badge, and AI recommendation badge.
- Replaced Catalyst/Risk boxes with 🐂 Bull Case / 🐻 Bear Case boxes (fall
  back to old catalyst/risk text if new fields absent on cached results).

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
Derives current share price from shareOutstanding (company profile)
and computes implied price targets for each DCF scenario. Each cell
now shows market cap, price target, and % upside.

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
…unavailable

Finnhub often omits fcfMarginTTM (e.g. NVDA), causing FCF to default to 0 and
the gross_margin*0.15 floor to severely underestimate intrinsic value. Adding
net_margin*0.85 as an intermediate proxy (FCF ≈ 85% of net income) fixes this:
for NVDA (~55% net margin) fcf_0 goes from 0.107 to 0.468, producing realistic
bear/base/bull price targets instead of -90%+ downsides.

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
Previous push via GitHub API encoded newlines as literal \n characters,
putting the entire file on one line and causing a SyntaxError on startup.
Restoring the correct version with proper newlines.

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
For high-growth stocks appearing in sleepers (e.g. NVDA with 65.5% rev growth),
the base case was using uncapped rev_growth while the bull case was capped at 30%.
This made base > bull (e.g. $15T base vs $5.8T bull for NVDA).

Fix: cap base at min(rev_growth*0.80, 20%) and bear at min(rev_growth*0.30, base),
enforcing monotonic ordering. This also correctly signals that a high-growth
stock is overvalued as a sleeper (NVDA sleeper base ~$3.2T vs $5.2T market cap).

https://claude.ai/code/session_0118U48tD3eV3sNjsCUvrmch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants