Home

Dividend Discount Model

I find it useful to translate mathematical concepts and procedures into code because it verifies that I understand what I’ve learnt and the code fits better with my mental models. Recently I’ve been doing this with the Dividend Discount Model (DDM).

Gordon Growth Model

This model is the basic DDM. It assumes a stable dividend growth rate, meaning it is only applicable in cases where the firm being valued is not expected to go through any supernormal growth: typically large, mature firms.

Some terms:

  • \(D\): estimated dividends for next year
  • \(r\): cost of capital
  • \(g\): estimated growth rate
def ggm(D, r, g):
  return (D * (1+g)) / (r-g)

ggm(D, r, g)
94.49999999999999

Multistage DDM

The multistage DDM is suitable for more complicated valuations where the security being analysed is expected to go through nonlinear growth, typically a stage of rapid growth in the short term, followed by slower growth in the mid-long term.

The standard DDM is unsuitable for situations such as these, so we must expand the DDM to factor multiple stages.

We can begin by simply defining the DDM in this case for a single time period.

def ddm(D, r, g, ex):
    """Calculates the discounted dividend for a given future time."""
    numerator = D * (1+g) ** ex
    denominator = (1+r)**ex
    return numerator/denominator

ddm(D, r, g, 3)
2.6366074851641255

Here is our progression. Years 1-3 contain rapid growth at 25%. The taper starts at 20% between 4-6, before declining to 15% for years 7-9. From year 10 onwards, growth is expected to be at 9%.

1 .25
4 .20
7 .15
10 .09

This table is turned into a python dictionary to achieve the format we desire for the DDM:

data = dict(data_table)

The above function ddm will calculate for a single period only (1 year). For the above progression it is convenient to calculate for a range of years, or timespan. ddm_for achieves this by accept an iterable span as parameter.

def ddm_for(D, r, g, span):
    """Calculates the discounted dividend for a constant dividend over a timespan."""
    return (ddm(D, r, g, span.index(x)+1) for x in span)

sum(ddm_for(D, r, g, range(4, 6)))
4.597568482610033

For each given period, the input data contains only the start year (what will be the dictionary key). So for each key, we must pad out the remaining years to produce the full timespan that a growth rate is applicable to. This can be resolved by looking at the nextmost key and decrementing by one. The resultant range is the applicable timespan.

def create_span(progression, key):
    """Creates the full timespan from a given key in the progression."""
    next_stage = min(k for k in progression if k > key and k != key)
    return range(key, next_stage - 1)

create_span(data.keys(), 4)
range(4, 6)

When calculating the current dividend to discount for a point in the progression, we need to know the exponent of that dividend. This is either the length of the timespan (in years), or 1 if we are at the start of the progression.

def dividend_exponent(progression, key):
    """Returns the dividend exponent for a given point in the progression"""
    keys = list(progression.keys())
    span = create_span(
        keys,
        key if keys.index(key) == 0 else max(k for k in keys if k < key))

    return 1 if keys.index(key) == 0 else len(span)+1

dividend_exponent(data, 4)
3

We can then apply this exponent to find the dividend, from the original value \(D\), at a given point in the progression.

def get_dividend(D, progression, key):
    steps = list(progression.keys())

    if steps.index(key) == 0:
        return D

    prev_key = max(k for k in steps if k < key)
    prev_g = progression[prev_key]
    g = progression[key]
    prev_g_exp = dividend_exponent(progression, key)
    prev_dividend = get_dividend(D, progression, prev_key)
    return prev_dividend * ((1+prev_g)**prev_g_exp)

round(get_dividend(2, data, 10), 2)
10.27

Python has no built-in function analogous to System.Linq.SelectMany, so flatten is provided here instead. We will use this to flatten the nested lists produced by the multistage DDM.

import itertools

def flatten(list_of_lists):
    "Flatten one level of nesting"
    return itertools.chain.from_iterable(list_of_lists)

The final stage of a multistage DDM is to discount into the future indefinitely, this function handles that case by applying the reduced form DDM.

def infinite_ddm(D, r, g, ex):
    """"""
    return (D*(1+g) / (r - g)) / ((1+r)**ex)

steps = list(data.keys())
last_step = steps[-1]
D = get_dividend(2, data, last_step)
round(infinite_ddm(D, .14, data[last_step], last_step-1), 2)
68.82

The final piece is to sum for all but the final period the result of ddm_for, and then add the infinite DDM for the final period. The result is the “true” value, given non-linear growth.

def multistage_ddm(D, r, progression):
    steps = list(progression.keys())
    infinite_step = steps[-1]

    infinite_result = infinite_ddm(
        get_dividend(D, progression, infinite_step),
        r,
        progression[infinite_step],
        infinite_step-1)

    values = (ddm_for(
        get_dividend(D, progression, k),
        r,
        progression[k],
        create_span(steps, k)) for k in steps[:-1])

    return sum(flatten(values)) + infinite_result

round(multistage_ddm(2, .14, data), 2)
95.54