File size: 6,474 Bytes
9fe4fad a746412 9fe4fad a746412 9fe4fad a746412 9fe4fad a746412 9fe4fad a746412 9fe4fad a746412 9fe4fad | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | ---
title: Analytics Notes
summary: DCF and beta implementation notes that sit close to the analytics code but deserve a stable docs entrypoint.
---
# Analytics Notes
These notes mirror the implementation guidance kept near the analytics code so
maintainers can reach it from the formal docs site as well.
## DCF
DCF code lives under `src/TerraFin/analytics/analysis/fundamental/dcf/`.
| Model | What it is trying to answer | Main checked-in data |
|------|------------------------------|----------------------|
| S&P 500 DCF | A year-end target framework | `fundamental/dcf/sp500_defaults.json` |
| Stock DCF | A spot equity valuation framework | derived from live company inputs plus explicit defaults |
### S&P 500 DCF
The index model is a year-end target model, not a spot fair-value model.
Refresh `sp500_defaults.json` in this order:
1. set a defensible public strategist target range
2. refresh the nominal base-year EPS context
3. refresh the explicit growth fade schedule
4. refresh payout and buyback assumptions
5. refresh ERP and terminal discounting assumptions
6. verify the resulting target still lands in a reasonable public range
### Stock DCF
The stock model needs one starting growth assumption (`Base Growth %`) and one
starting cash-flow level (`Base FCF / Share`). Both can be overridden; both have
fallback cascades when blank.
Current fallback order for `Base Growth %` in `build_stock_template()`:
1. user override
2. EPS growth from `forwardEps` versus `trailingEps`
3. annual revenue CAGR
4. annual FCF CAGR
5. default `6%`
Why revenue comes before FCF CAGR:
- revenue is usually more stable
- FCF is more sensitive to capex timing and working-capital noise
- revenue is often the better default growth anchor when explicit EPS guidance is missing
#### Base FCF source cascade
`Base FCF / Share` is selected by `_select_stock_fcf_base()` in
`src/TerraFin/analytics/analysis/fundamental/dcf/inputs.py`. Four sources, with
explicit picks via the `fcf_base_source` override on `StockDCFOverrides`:
| Source | Helper | Notes |
|---|---|---|
| `auto` *(default)* | cascade | 3yr_avg β annual β ttm β missing |
| `3yr_avg` | `_three_year_avg_fcf` | Mean of last 3 valid annual FCF/share rows. Returns `None` if fewer than 2 valid years. |
| `latest_annual` | `_latest_annual_fcf` | First non-NaN annual FCF/share row. |
| `ttm` | `_quarterly_ttm_fcf` | Sum of last 4 quarterly FCF rows; `None` if fewer than 4 valid quarters. |
The `auto` cascade prefers normalized over recent because DCF capitalizes the
base into perpetuity β single-period TTM can be distorted by working-capital
swings or one-off capex. McKinsey *Valuation* explicitly recommends a
multi-year normalized FCF as the DCF base.
Explicit `latest_annual`/`ttm`/`3yr_avg` picks **do not fall back** when the
chosen source has no data: the call returns `(None, "missing")` so the UI can
surface an accurate insufficient-data message instead of silently using a
different basis.
A user-supplied `base_cash_flow_per_share` override always wins over the source
cascade.
#### Projection horizon
`projection_years` (5 / 10 / 15) controls the explicit forecast length. The
treasury curve is sampled accordingly (`yield_at(1)` β¦ `yield_at(N)`); the
terminal discount rate uses the 30-year long-term rate regardless of horizon.
#### Turnaround mode
When all three turnaround fields are supplied β `breakeven_year`,
`breakeven_cash_flow_per_share`, `post_breakeven_growth_pct` β the template
builds an explicit per-year FCF schedule via `_build_turnaround_schedule()`
instead of the single-base Γ growth-curve path. Schedule shape:
- **Years 1 β¦ breakeven_year**: linear interpolation from the *current* TTM
FCF/share (which may be negative β that's the whole point) to
`breakeven_cash_flow_per_share` at year `breakeven_year`.
- **Years breakeven_year+1 β¦ horizon**: compound at `post_breakeven_growth_pct`
with a linear fade toward `terminal_growth_pct` across the remaining years.
Negative cash flows in the early years are reflected honestly in the DCF: each
year's present value is summed, so cash-burn periods reduce intrinsic value.
Status flips to `ready` when the year-N (horizon) FCF is positive; otherwise
the template stays at `insufficient_data` with an explanatory warning.
Bear/base/bull scenario shifts in turnaround mode apply a cumulative
year-over-year compounding bump (`growth_shift_pct` per year) so the three
scenarios diverge meaningfully even on a user-supplied schedule.
Related defaults:
- `Base FCF / Share`: source cascade (see above)
- `Beta`: provider beta first, then TerraFin's computed beta, then `1.0`
- `Equity Risk Premium %`: default `5.0%`
- `Terminal Growth %`: default `3.0%`
- `Projection Years`: default `5`
- `FCF Base Source`: default `auto`
Override the automatic growth input when:
- the business is in a regime shift
- provider EPS is stale
- revenue CAGR is acquisition-driven
- FCF is distorted by one-off capex or working-capital swings
- the company is a financial firm where FCF-style DCF is not a good framing
Use turnaround mode (instead of overrides) when:
- current FCF is negative but the investment thesis is on a future turn
- you need the schedule's losses to count against intrinsic value rather than
being silently clipped to zero
- you can name a defensible breakeven year (typically 1β5 for operational
turnarounds; longer is usually better modeled as a different framing)
## Beta
Risk code lives under `src/TerraFin/analytics/analysis/risk/`.
Current built-in methods:
- `beta_5y_monthly`
- `beta_5y_monthly_adjusted`
### Default Method
`beta_5y_monthly` is the reference method:
- lookback: 5 years
- frequency: month-end closes
- formula: `Cov(stock_returns, benchmark_returns) / Var(benchmark_returns)`
`beta_5y_monthly_adjusted` is the stability variant:
- start from `beta_5y_monthly`
- shrink toward `1.0`
- formula: `0.67 * beta_5y_monthly + 0.33 * 1.0`
### Benchmark Mapping
| Market | Benchmark |
|--------|-----------|
| U.S. / default | `^SPX` |
| Korea `.KS` | `^KS11` |
| Korea `.KQ` | `^KQ11` |
| Japan `.T` | `^N225` |
If TerraFin cannot map a ticker confidently, it returns
`unsupported_benchmark` instead of forcing a proxy.
## Code-Adjacent Source
The full code-adjacent source remains on GitHub:
[src/TerraFin/analytics/analysis/README.md](https://github.com/KiUngSong/TerraFin/blob/main/src/TerraFin/analytics/analysis/README.md)
|