Apple Revenue LGD: 4 Powerful Insights Into Recovery

Written by

in

In this post, part of my revenue risk series, I introduce an Apple revenue LGD model to measure the recovery following a revenue impairment. The objective is to estimate Loss Given Default (LGD) using historical Apple revenue data. The concepts used here are borrowed terminology from credit risk modeling. I’m applying the principles of credit risk frameworks to model large revenue reductions, instead of principal defaults.

In the previous post, Apple Revenue Risk: Estimating Probability of Default, I identified historical revenue impairments and calculated a 4-quarter realized impairment frequency (PD₁). A revenue impairment occurs when seasonally adjusted quarter-on-quarter revenue growth falls below -10%.

In this post, I extend the framework by measuring how revenue recovers following an impairment. This introduces a second dimension of Apple revenue risk: the severity of the remaining revenue shortfall.

This post covers the implementation in Python:

  • Defining the 4-quarter LGD recovery window
  • Calculating the relative revenue shortfall
  • Estimating terminal Loss Given Default (LGD)

Defining the Apple revenue LGD recovery window

In credit risk, Loss Given Default measures the severity of a loss conditional on a default having occurred. For revenue risk, I use the same principle to measure the remaining revenue shortfall following an impairment.

As I used a 4-quarter horizon for PD₁, I apply the same horizon to measuring recovery. Each LGD window starts in the quarter of impairment (td) and includes the following three quarters.

I use the dataset prepared in the previous post, containing seasonally adjusted revenue, revenue growth, impairment events, and expected revenue. The expected revenue is my Exposure at Default (EAD) proxy, defined as the seasonally adjusted revenue of the quarter preceding impairment.

For each impairment event, EAD remains fixed throughout the recovery window. This provides a consistent reference point for measuring how much of the initial revenue level has been recovered.

Show LGD recovery window code
# LGD window: the impairment quarter t_d and the next three quarters.
# Use adjusted revenue from t_d - 1 as fixed EAD. The default-quarter
# shortfall is therefore the decline measured by adjusted QoQ growth.

lgd_data = adjusted.sort_values(
    "quarter_id", kind="stable"
).reset_index(drop=True)

lgd_defaults = (
    lgd_data.loc[
        lgd_data["impairment"].eq(True),
        ["period", "quarter_id", "expected_revenue"],
    ]
    .rename(columns={
        "period": "default_quarter",
        "quarter_id": "default_quarter_id",
        "expected_revenue": "ead_at_default",
    })
)

lgd_observations = lgd_data[
    [
        "quarter_id",
        "period",
        "impairment",
        "revenue_growth_sa",
        "revenue_sa",
        "revenue",
    ]
].rename(columns={
    "quarter_id": "observation_quarter_id",
    "period": "observation_quarter",
    "revenue_sa": "observed_revenue_sa",
})

# Join each default to the default quarter
# and the next three calendar quarters.

lgd_paths = lgd_defaults.merge(
    pd.DataFrame({"horizon_quarter": range(1, 5)}),
    how="cross",
)

lgd_paths["observation_quarter_id"] = (
    lgd_paths["default_quarter_id"]
    + lgd_paths["horizon_quarter"]
    - 1
)

lgd_paths = lgd_paths.merge(
    lgd_observations,
    on="observation_quarter_id",
    how="left",
    validate="many_to_one",
)

# Require four observed quarters.

complete_lgd_window = (
    lgd_paths.groupby("default_quarter_id")["observed_revenue_sa"]
    .transform("count")
    .eq(4)
)

lgd_paths = (
    lgd_paths.loc[complete_lgd_window]
    .sort_values(["default_quarter_id", "horizon_quarter"])
    .reset_index(drop=True)
)

The chart below illustrates an LGD recovery window. The horizontal line represents EAD at the moment of impairment, while the observed seasonally adjusted revenue shows how revenue develops during the following quarters.

Show LGD recovery window chart code
# Illustrate complete window with an observed EAD.
illustrative_default = pd.Period('2018Q4', 'Q-DEC')

if illustrative_default is not None:

    illustrative_window = lgd_paths.loc[
        lgd_paths["default_quarter"].eq(illustrative_default)
    ]

    horizon = illustrative_window["horizon_quarter"].to_numpy()
    observed = illustrative_window["observed_revenue_sa"].to_numpy()
    ead_at_default = illustrative_window["ead_at_default"].iloc[0]

    fig, ax = plt.subplots(figsize=(9, 5))

    ax.plot(
        horizon,
        observed,
        color="#0756b1",
        marker="o",
        lw=2.5,
        label="Observed seasonally adjusted revenue",
    )

    ax.axhline(
        ead_at_default,
        color="#d52b24",
        ls="--",
        lw=2,
        label="EAD at default (fixed)",
    )

    ax.fill_between(
        horizon,
        observed,
        ead_at_default,
        where=observed < ead_at_default,
        color="#d52b24",
        alpha=0.15,
    )

    ax.set_xticks(
        horizon,
        illustrative_window["observation_quarter"].astype(str),
    )

    ax.set_ylabel("Seasonally adjusted revenue (M USD)")

    ax.set_title(
        f"AAPL LGD observation window: default {illustrative_default}",
        loc="left",
        fontweight="bold",
    )

    ax.grid(axis="y", alpha=0.3)
    ax.spines[["top", "right"]].set_visible(False)
    ax.legend(frameon=False)

    fig.tight_layout()

    fig.savefig(
        "aapl_revenue_lgd_window.png",
        dpi=180,
        bbox_inches="tight",
    )

    plt.show()

else:
    print("AAPL LGD: no complete four-quarter window has an observed EAD.")

Whenever observed revenue falls below EAD, the difference represents a revenue shortfall. A recovery occurs when this difference decreases over time. Once observed revenue reaches or exceeds EAD, the shortfall is considered fully recovered.

Calculating the relative revenue shortfall

The next step in the Apple revenue LGD calculation is to measure the relative revenue shortfall for each quarter in the recovery window.

I measure this as the difference between EAD at default and observed seasonally adjusted revenue, divided by EAD at default. Negative shortfalls are set to zero, as revenue exceeding the reference level is considered a full recovery rather than a negative loss.

For example, if EAD is 100 and observed revenue is 85, the relative shortfall is 15%. If revenue subsequently recovers to 95, the remaining shortfall decreases to 5%.

This measure allows me to compare recovery paths across different impairment events in the Apple revenue LGD framework, regardless of the absolute revenue level at the time of impairment.

Show relative revenue shortfall calculation
# Relative shortfall: unrecovered adjusted revenue as a share of fixed EAD.

lgd_paths["relative_shortfall"] = (
    (
        lgd_paths["ead_at_default"]
        - lgd_paths["observed_revenue_sa"]
    )
    / lgd_paths["ead_at_default"]
).clip(lower=0)

lgd_paths = (
    lgd_paths.sort_values(
        ["default_quarter_id", "horizon_quarter"]
    )
    .loc[:, [
        "default_quarter",
        "horizon_quarter",
        "observation_quarter",
        "impairment",
        "revenue_growth_sa",
        "ead_at_default",
        "observed_revenue_sa",
        "revenue",
        "relative_shortfall",
    ]]
    .reset_index(drop=True)
)

The following chart shows an example of the relative shortfall trajectory following a revenue impairment.

Show relative revenue shortfall chart code
if illustrative_default is not None:

    illustrative_shortfall = lgd_paths.loc[
        lgd_paths["default_quarter"].eq(illustrative_default)
    ]

    horizon = illustrative_shortfall["horizon_quarter"].to_numpy()
    shortfall = illustrative_shortfall["relative_shortfall"].to_numpy()

    fig, ax = plt.subplots(figsize=(9, 5))

    ax.plot(
        horizon,
        shortfall,
        color="#d52b24",
        marker="o",
        lw=2.5,
    )

    ax.fill_between(
        horizon,
        shortfall,
        color="#d52b24",
        alpha=0.15,
    )

    ax.set_xticks(
        horizon,
        illustrative_shortfall["observation_quarter"].astype(str),
    )

    ax.yaxis.set_major_formatter(PercentFormatter(1))
    ax.set_ylabel("Revenue shortfall / EAD at default")

    ax.set_title(
        f"AAPL relative revenue shortfall: default {illustrative_default}",
        loc="left",
        fontweight="bold",
    )

    ax.grid(axis="y", alpha=0.3)
    ax.spines[["top", "right"]].set_visible(False)

    fig.tight_layout()

    fig.savefig(
        "aapl_revenue_lgd.png",
        dpi=180,
        bbox_inches="tight",
    )

    plt.show()

The relative shortfall represents the proportion of EAD that remains unrecovered at each point in time. A declining shortfall indicates recovery, while an increasing shortfall indicates that revenue has moved further below the reference level.

Importantly, this is a period-specific measure rather than a cumulative sum of revenue losses. It measures the remaining shortfall in each quarter relative to the fixed EAD at default.

Estimating terminal Loss Given Default (LGD)

The final step in the Apple revenue LGD framework is to estimate terminal Loss Given Default (LGD). I define terminal LGD as the relative revenue shortfall observed in the fourth and final quarter of the recovery window.

This provides a simple measure of the remaining revenue impairment after one year. If revenue has fully recovered to its EAD reference level, LGD equals 0%. Otherwise, LGD represents the proportion of EAD that remains unrecovered.

The calculation is deliberately different from the cumulative loss over the entire recovery period. It measures the remaining shortfall at the end of the horizon, not the sum of shortfalls experienced during the preceding quarters.

Show terminal LGD calculation
# Terminal LGD: the relative shortfall in the fourth observed quarter.

if lgd_paths.empty:

    print(
        "AAPL LGD: no impairment has a complete "
        "four-quarter observation window."
    )

else:

    # The terminal shortfall, including zero after full recovery, is LGD.

    lgd_history = (
        lgd_paths.loc[
            lgd_paths["horizon_quarter"].eq(4),
            [
                "default_quarter",
                "observation_quarter",
                "ead_at_default",
                "relative_shortfall",
            ],
        ]
        .rename(columns={
            "observation_quarter": "horizon_end",
            "relative_shortfall": "lgd",
        })
        .reset_index(drop=True)
    )

    print(
        "AAPL LGD: last observed relative revenue "
        "shortfall over four quarters"
    )

    print(
        lgd_history.to_string(
            index=False,
            formatters={
                "ead_at_default": lambda x: f"{x:,.0f}",
                "lgd": lambda x: f"{x:.1%}",
            },
        )
    )

    measured_lgd = lgd_history.loc[
        lgd_history["lgd"].notna()
    ]

Using the seven identified Apple revenue impairments, I obtain the following terminal LGD observations:

default_quarter horizon_end ead_at_default  lgd
2012Q2          2013Q1         37,101       0.0%
2013Q2          2014Q1         40,425       0.0%
2016Q1          2016Q4         54,109       0.0%
2017Q1          2017Q4         59,175       0.0%
2018Q1          2018Q4         66,683      12.0%
2018Q4          2019Q3         71,361       2.2%
2022Q4          2023Q3         95,623       0.7%

Four of the seven impairment events resulted in a terminal LGD of 0%, meaning that seasonally adjusted revenue had fully recovered to its fixed EAD reference level by the end of the recovery window.

The remaining three events show a positive terminal LGD, with the highest observed shortfall occurring following the 2018-Q1 impairment.

To summarize these observations, I calculate the mean, median, minimum, and maximum terminal LGD.

Show terminal LGD statistics code
if measured_lgd.empty:

    print(
        "Mean LGD: unavailable; no complete "
        "default window has an EAD."
    )

else:

    terminal_lgd = measured_lgd["lgd"]

    print(
        f"Terminal LGD statistics "
        f"({len(terminal_lgd)} complete windows with EAD): "
        f"mean {terminal_lgd.mean():.1%}, "
        f"median {terminal_lgd.median():.1%}, "
        f"min {terminal_lgd.min():.1%}, "
        f"max {terminal_lgd.max():.1%}"
    )
Terminal LGD statistics (7 complete windows with EAD):

Mean:    2.1%
Median:  0.0%
Minimum: 0.0%
Maximum: 12.0%

The historical mean terminal LGD is 2.1%, while the median is 0.0%. These Apple revenue LGD results suggest that most identified impairments were followed by a full recovery to the fixed EAD reference level within the defined horizon.

However, the observations also show that recovery is not guaranteed. In some cases, a meaningful revenue shortfall remains after four quarters.

These results should be interpreted as descriptive historical recovery outcomes rather than predictive LGD estimates. The sample contains only seven impairment events, and the fixed EAD reference is a deliberately simple counterfactual that does not incorporate the revenue growth that might otherwise have occurred.

Terminal LGD does not capture the revenue shortfalls experienced earlier in the recovery window. It measures the remaining impairment at the end of the horizon rather than the total revenue forgone following default.

What comes next?

With historical PD₁ and Apple revenue LGD measures established, the next step is to develop predictive models using relevant economic indicators.

For this, I’ll use Federal Reserve Economic Data (FRED), maintained by the Federal Reserve Bank of St. Louis. FRED provides access to a large collection of historical economic and financial time series, including interest rates, consumer confidence, unemployment, and industrial production.

FRED is useful because these indicators may contain information about changing economic conditions before their effects become visible in company revenue. Its standardized historical data and Python-accessible API also make it convenient to retrieve and compare potential leading indicators.

In the next post, I’ll explore whether selected FRED indicators can help predict the occurrence and severity of Apple’s revenue impairments, moving from historical measurements toward a predictive revenue risk framework.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.