#!/usr/bin/python3
# -*- coding: utf-8 -*-
# ... or run online through https://repl.it/languages/python3
import math
DIST_DIV = 0.03
DIST_GAIN = 0.07
YEARS = range(1, 11)
class Holding:
def __init__(self, shares, nav, div, gain):
self.shares = shares
self.nav = nav
self.cash = 0.0
self.__div = div
self.__gain = gain
def balance(self):
return self.shares * self.nav
def annual(self):
dividend = (self.shares * self.nav) * self.__div
self.nav *= 1 + self.__gain
return dividend
def buy(self, value):
frac_share, new_shares = math.modf(value / self.nav)
self.cash += frac_share * self.nav
self.shares += new_shares
while self.cash > self.nav:
self.shares += 1
self.cash -= self.nav
def sell(self, value):
sale_shares = math.floor(value / self.nav)
self.cash = sale_shares * self.nav
self.shares -= sale_shares
dist = Holding(10000, 1.0, DIST_DIV, DIST_GAIN)
accm = Holding(10000, 1.0, 0.0, DIST_DIV + DIST_GAIN)
# Returns: (year,
# accm.nav, accm.shares, accm.balance(), accm.cash,
# dist.nav, dist.shares, dist.balance(), dist.cash)
def record(year=None):
return (year,
accm.nav, accm.shares, accm.balance(), accm.cash,
dist.nav, dist.shares, dist.balance(), dist.cash)
# Returns: [(year,
# accm.nav, accm.shares, accm.balance(), accm.cash,
# dist.nav, dist.shares, dist.balance(), dist.cash), ...]
def accumulation():
data = [record()]
for year in YEARS:
dividend = dist.annual()
accm.annual()
dist.buy(dividend)
data += [record(year)]
return data
# Returns: [(year,
# accm.nav, accm.shares, accm.balance(), accm.cash,
# dist.nav, dist.shares, dist.balance(), dist.cash), ...],
# accm_total, dist_total
def decumulation():
data = [record()]
(dist_total, accm_total) = (0.0, 0.0)
for year in YEARS:
dist.cash = dist.annual()
accm.annual()
accm.sell(dist.cash)
data += [record(year)]
accm_total += accm.cash
dist_total += dist.cash
return data, accm_total, dist_total
# Returns: ['line', ...] in wikitable format
def format_wiki_table(table_data, phase):
def __currency(value, places):
string = ('€{:,.%df}' % places).format(value)
if value > 100 and value < 1000:
return '{{0}}' + string
else:
return string
if phase == 'accumulation':
cash = 'Cash balance'
elif phase == 'decumulation':
cash = 'Cash withdrawal'
table = [
'{| class="wikitable mw-datatable" style="text-align: center;"',
'|+ Comparison of accumulating and distributing ETF investor outcomes,'
' %s phase' % phase,
'|-',
'! scope="col" colspan="1" |',
'! scope="col" colspan="4" | Bob: Accumulating ETF investor',
'! scope="col" colspan="4" | Alice: Distributing ETF investor',
'|-',
'! scope="col" | Year',
'! scope="col" | ACCM net asset value',
'! scope="col" | Shares held',
'! scope="col" | Holding balance',
'! scope="col" | %s' % cash,
'! scope="col" | DIST net asset value',
'! scope="col" | Shares held',
'! scope="col" | Holding balance',
'! scope="col" | %s' % cash
]
for elements in table_data:
table += ['|-']
year = elements[0]
accm_cash = ' || %s' % __currency(elements[4], 2) if year else ' ||'
dist_cash = ' || %s' % __currency(elements[8], 2) if year else ' ||'
table += [('! %d' % year if year else '!')]
table += [' | %s' % __currency(elements[1], 4)
+ ' || %d' % elements[2]
+ ' || %s' % __currency(elements[3], 2)
+ accm_cash
+ ' || %s' % __currency(elements[5], 4)
+ ' || %d' % elements[6]
+ ' || %s' % __currency(elements[7], 2)
+ dist_cash
]
table += ['|}']
return table
def main():
data = accumulation()
for line in format_wiki_table(data, 'accumulation'):
print(line)
print()
(dist.cash, accm.cash) = (0.0, 0.0)
(data, accm_total, dist_total) = decumulation()
for line in format_wiki_table(data, 'decumulation'):
print(line)
print()
print('Dist total withdrawal =', round(dist_total, 2))
print('Accm total withdrawal =', round(accm_total, 2))
print()
print('Dist investor result =',
round(dist.shares * dist.nav + dist_total, 2))
print('Accm investor result =',
round(accm.shares * accm.nav + accm_total, 2))
if __name__ == '__main__':
main()