Return Decomposition
The Return Decomposition splits the share price return over a window into the growth of the Earnings per Share and the change of the Price-to-Earnings ratio (the multiple), and adds the dividends paid over the window for the total return. It shows whether a share rose because the company earned more or because investors paid more for the same earnings, e.g. a share whose earnings beat expectations but whose price fell because its multiple contracted.
Since the share price equals the Earnings per Share times the P/E ratio, the price relative over a window is the product of the earnings relative and the multiple relative, which is the earnings and valuation split of equity returns used in return attribution (e.g. Bogle, “Common Sense on Mutual Funds”, and the building blocks of Grinold and Kroner, “The Equity Risk Premium”, 2002).
The formula is as follows:
\[1 + \text{Price Return} = (1 + \text{EPS Growth}) \cdot (1 + P / E \;\text{Change})\] \[\text{EPS Growth} = \text{EPS}_{t} / \text{EPS\_t-lag} - 1\] \[P / E \;\text{Change} = (\text{Price}_{t} / \text{EPS}_{t}) / (\text{Price\_t-lag} / \text{EPS\_t-lag}) - 1\] \[\text{Dividend Return} = \text{Dividends per Share paid over the window} / \text{Price\_t-lag}\] \[\text{Total Return} = \text{Price Return} + \text{Dividend Return}\]With logarithmic=True the parts are log returns that add up: ln(1 + Total Return) = ln(1 + EPS Growth) + ln(1 + P/E Change) + ln(1 + Dividends / Price_t).
Also known as: earnings and multiple decomposition, P/E expansion and contraction, multiple expansion, return attribution by earnings growth and re-rating.
Return Decomposition in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_return_decomposition as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["AMD", "MSFT"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2021-01-01",
end_date="2025-12-31",
)
toolkit.models.get_return_decomposition()
Or, after setting up the Finance Toolkit MCP, ask:
How much of the share price returns of AMD and Microsoft from 2022 to 2025 came from earnings growth and how much from a change in their P/E?
Which returns:
| 2022 | 2023 | 2024 | 2025 | |
|---|---|---|---|---|
| (‘AMD’, ‘Price Return’) | -0.5499 | 1.2759 | -0.1806 | 0.773 |
| (‘AMD’, ‘EPS Growth’) | -0.6734 | -0.3745 | 0.9075 | 1.6433 |
| (‘AMD’, ‘P/E Change’) | 0.3782 | 2.6387 | -0.5704 | -0.3292 |
| (‘AMD’, ‘Dividend Return’) | 0 | 0 | 0 | 0 |
| (‘AMD’, ‘Total Return’) | -0.5499 | 1.2759 | -0.1806 | 0.773 |
| (‘MSFT’, ‘Price Return’) | -0.2869 | 0.568 | 0.1209 | 0.1474 |
| (‘MSFT’, ‘EPS Growth’) | 0.1979 | 0.0039 | 0.2185 | 0.156 |
| (‘MSFT’, ‘P/E Change’) | -0.4047 | 0.562 | -0.0801 | -0.0075 |
| (‘MSFT’, ‘Dividend Return’) | 0.0076 | 0.0116 | 0.0082 | 0.0081 |
| (‘MSFT’, ‘Total Return’) | -0.2794 | 0.5796 | 0.1291 | 0.1554 |
Parameters
get_return_decomposition accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
lag | int | None | The number of periods the window spans. Defaults to None, which compares each period with the same period a year earlier: 1 with annual data and 4 with quarterly data. | 4 |
diluted | bool | Whether to use the diluted Earnings per Share. Defaults to True. | False |
trailing | int | None | The number of periods to sum the Net Income over (and average the shares over). Defaults to None, which uses the trailing twelve months (4 quarters) with quarterly data and single years with annual data, as the P/E ratio does. | 4 |
logarithmic | bool | Whether to return log returns, which add up, instead of simple returns, which multiply. Defaults to False. | False |
rounding | int | None | The number of decimals to round the results to. Defaults to None, which uses the rounding of the Toolkit (4 unless set otherwise). | 2 |