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)