Talk:Comparison of accumulating ETFs and distributing ETFs

Active discussions

Python table generation

The tables in this page were generated using a Python script. The source code for this script is below.

If Python is not available locally, run the script online with Repl.it, or similar. For example, for Repl.it, copy-and-paste the code into the source code editing window (left panel), and press "Run>". The output window (right panel) will contain the table formatted in wikitext.

Code listing
  1#!/usr/bin/python3
  2# -*- coding: utf-8 -*-
  3# ... or run online through https://repl.it/languages/python3
  4
  5import math
  6
  7DIST_DIV = 0.03
  8DIST_GAIN = 0.07
  9YEARS = range(1, 11)
 10
 11
 12class Holding:
 13    def __init__(self, shares, nav, div, gain):
 14        self.shares = shares
 15        self.nav = nav
 16        self.cash = 0.0
 17        self.__div = div
 18        self.__gain = gain
 19
 20    def balance(self):
 21        return self.shares * self.nav
 22
 23    def annual(self):
 24        dividend = (self.shares * self.nav) * self.__div
 25        self.nav *= 1 + self.__gain
 26        return dividend
 27
 28    def buy(self, value):
 29        frac_share, new_shares = math.modf(value / self.nav)
 30        self.cash += frac_share * self.nav
 31        self.shares += new_shares
 32        while self.cash > self.nav:
 33          self.shares += 1
 34          self.cash -= self.nav
 35
 36    def sell(self, value):
 37        sale_shares = math.floor(value / self.nav)
 38        self.cash = sale_shares * self.nav
 39        self.shares -= sale_shares
 40
 41
 42dist = Holding(10000, 1.0, DIST_DIV, DIST_GAIN)
 43accm = Holding(10000, 1.0, 0.0, DIST_DIV + DIST_GAIN)
 44
 45
 46# Returns: (year,
 47#          accm.nav, accm.shares, accm.balance(), accm.cash,
 48#          dist.nav, dist.shares, dist.balance(), dist.cash)
 49
 50def record(year=None):
 51    return (year,
 52         accm.nav, accm.shares, accm.balance(), accm.cash,
 53         dist.nav, dist.shares, dist.balance(), dist.cash)
 54
 55
 56# Returns: [(year,
 57#           accm.nav, accm.shares, accm.balance(), accm.cash,
 58#           dist.nav, dist.shares, dist.balance(), dist.cash), ...]
 59
 60def accumulation():
 61    data = [record()]
 62    for year in YEARS:
 63        dividend = dist.annual()
 64        accm.annual()
 65        dist.buy(dividend)
 66        data += [record(year)]
 67    return data
 68
 69
 70# Returns: [(year,
 71#           accm.nav, accm.shares, accm.balance(), accm.cash,
 72#           dist.nav, dist.shares, dist.balance(), dist.cash), ...],
 73#           accm_total, dist_total
 74
 75def decumulation():
 76    data = [record()]
 77    (dist_total, accm_total) = (0.0, 0.0)
 78    for year in YEARS:
 79        dist.cash = dist.annual()
 80        accm.annual()
 81        accm.sell(dist.cash)
 82        data += [record(year)]
 83        accm_total += accm.cash
 84        dist_total += dist.cash
 85    return data, accm_total, dist_total
 86
 87
 88# Returns: ['line', ...] in wikitable format
 89
 90def format_wiki_table(table_data, phase):
 91
 92    def __currency(value, places):
 93        string = ('€{:,.%df}' % places).format(value)
 94        if value > 100 and value < 1000:
 95            return '{{0}}' + string
 96        else:
 97            return string
 98
 99    if phase == 'accumulation':
100        cash = 'Cash balance'
101    elif phase == 'decumulation':
102        cash = 'Cash withdrawal'
103
104    table = [
105        '{| class="wikitable mw-datatable" style="text-align: center;"',
106        '|+ Comparison of accumulating and distributing ETF investor outcomes,'
107        ' %s phase' % phase,
108        '|-',
109        '! scope="col" colspan="1" |',
110        '! scope="col" colspan="4" | Accumulating ETF investor',
111        '! scope="col" colspan="4" | Distributing ETF investor',
112        '|-',
113        '! scope="col" | Year',
114        '! scope="col" | ACCM net asset value',
115        '! scope="col" | Shares held',
116        '! scope="col" | Holding balance',
117        '! scope="col" | %s' % cash,
118        '! scope="col" | DIST net asset value',
119        '! scope="col" | Shares held',
120        '! scope="col" | Holding balance',
121        '! scope="col" | %s' % cash
122        ]
123    for elements in table_data:
124        table += ['|-']
125        year = elements[0]
126        accm_cash = ' || %s' % __currency(elements[4], 2) if year else ' ||'
127        dist_cash = ' || %s' % __currency(elements[8], 2) if year else ' ||'
128        table += [('! %d' % year if year else '!')]
129        table += [' | %s' % __currency(elements[1], 4)
130                  + ' || %d' % elements[2]
131                  + ' || %s' % __currency(elements[3], 2)
132                  + accm_cash
133                  + ' || %s' % __currency(elements[5], 4)
134                  + ' || %d' % elements[6]
135                  + ' || %s' % __currency(elements[7], 2)
136                  + dist_cash
137                 ]
138    table += ['|}']
139    return table
140
141
142def main():
143    data = accumulation()
144    for line in format_wiki_table(data, 'accumulation'):
145        print(line)
146    print()
147
148    (dist.cash, accm.cash) = (0.0, 0.0)
149    (data, accm_total, dist_total) = decumulation()
150    for line in format_wiki_table(data, 'decumulation'):
151        print(line)
152    print()
153
154    print('Dist total withdrawal =', round(dist_total, 2))
155    print('Accm total withdrawal =', round(accm_total, 2))
156    print()
157
158    print('Dist investor result =',
159          round(dist.shares * dist.nav + dist_total, 2))
160    print('Accm investor result =',
161          round(accm.shares * accm.nav + accm_total, 2))
162
163
164if __name__ == '__main__':
165    main()

--TedSwippet 09:57, 18 August 2019 (UTC)
Updated: --TedSwippet 15:21, 30 December 2020 (UTC)

Return to "Comparison of accumulating ETFs and distributing ETFs" page.