repl.it
@craq/

compare_ETF_with_a_basket_of_funds.py

Python

#compare investment strategies: #a) ETF #b) investing in a basket of a wide range of shares

fork
loading
Files
  • main.py
  • ETF_and_basket_final_distribution.png
  • fund_value_ration_ETF_basket_vs_fund_value.png
  • fund_value_ration_ETF_basket_vs_time.png
  • fund_value_vs_time.png
  • growth_rate_vs_fund_value.png
  • growth_rate_vs_time.png
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#compare investment strategies:
#a) ETF
#b) investing in a basket of a wide range of shares
#   Assumption: expected return of the wide range of shares should match the index, but with higher volatility

import numpy as np
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import random

import matplotlib.pyplot as plt

initial_investment = 1e5
annual_investment = 1e4
transaction_fee = 5  # per transaction
ETF_annual_fee = 0.1 / 100  # annual ETF management fee
annual_return = 8 / 100  # expected annual return
volatility_ETF = 4 / 100  # standard deviation of annual return
volatility_basket = 2 / 100  # additional deviation related to smaller number of shares in the basket than in ETF
n_shares_in_basket = 50
n_annual_rebalancing_transactions = 10
n_years = 50
n_repetitions = 500

ETF = np.empty((n_repetitions, n_years+1), dtype=np.double)
ETF_growth = np.empty((n_repetitions, n_years), dtype=np.double)
basket = np.empty((n_repetitions, n_years+1), dtype=np.double)
basket_growth = np.empty((n_repetitions, n_years), dtype=np.double)

for kk in range(n_repetitions):
    ETF[kk][0] = initial_investment - transaction_fee
    basket[kk][0] = initial_investment - n_shares_in_basket*transaction_fee
    for jj in range(n_years):
        ii = jj + 1
        market_volatility = random.gauss(0,volatility_ETF)

        ETF[kk][ii] = ETF[kk][jj]
        ETF[kk][ii] += ETF[kk][ii] * (annual_return + market_volatility)
        ETF[kk][ii] -= ETF[kk][ii] * ETF_annual_fee
        ETF[kk][ii] += annual_investment - transaction_fee
        ETF_growth[kk][jj] = (ETF[kk][ii] - annual_investment) / ETF[kk][jj]

        #basket volatility is related to the market volatility, but with additional randomness
        basket_volatility = market_volatility + random.gauss(0,volatility_basket)
        basket[kk][ii] = basket[kk][jj]
        basket[kk][ii] += basket[kk][ii] * (annual_return + basket_volatility)
        basket[kk][ii] -= transaction_fee * n_annual_rebalancing_transactions
        basket[kk][ii] += annual_investment #the annual investment should be used to rebalance the basket. Therefore the transaction fee is part of the line above and does not appear here
        basket_growth[kk][jj] = (basket[kk][ii] - annual_investment) / basket[kk][jj]

ETF_averaged = ETF.mean(axis=0)
ETF_growth_averaged = ETF_growth.mean(axis=0)
basket_averaged = basket.mean(axis=0)
basket_growth_averaged = basket_growth.mean(axis=0)

ETF_final_stats = ETF[:,-1]
basket_final_stats = basket[:,-1]

fig1=plt.figure()
plt.plot(list(range(0, n_years + 1)), ETF_averaged)
plt.plot(list(range(0, n_years + 1)), basket_averaged)
plt.xlabel('years')
plt.ylabel('fund value')
plt.yscale('log')
plt.legend(['ETF', 'basket'])

fig2=plt.figure()
plt.plot(list(range(0, n_years)), ETF_growth_averaged)
plt.plot(list(range(0, n_years)), basket_growth_averaged)
plt.xlabel('years')
plt.ylabel('annual growth')
plt.legend(['ETF', 'basket'])

fig3=plt.figure()
plt.plot(ETF_averaged[0:-1], ETF_growth_averaged)
plt.plot(basket_averaged[0:-1], basket_growth_averaged)
plt.xscale('log')
plt.xlabel('size of fund')
plt.ylabel('annual growth')
plt.legend(['ETF', 'basket'])


bin_min = min(min(ETF_final_stats), min(basket_final_stats))
bin_max = max(max(ETF_final_stats), max(basket_final_stats))
my_bins = np.linspace(bin_min,bin_max,min(40,round(n_repetitions/2)))
fig4=plt.figure()
plt.hist(ETF_final_stats,my_bins,alpha=0.5)
plt.hist(basket_final_stats,my_bins,alpha=0.5)
plt.legend(['ETF', 'basket'])
plt.title('distribution of fund values after ' + repr(n_years) + ' years with ETF fees of ' + repr(ETF_annual_fee*100) + '%p.a.')

fig5=plt.figure()
plt.plot(list(range(0, n_years + 1)), np.divide(ETF_averaged, basket_averaged))
plt.xlabel('years')
plt.ylabel('fund value ratio ETF/basket')

fig6=plt.figure()
plt.plot(ETF_averaged, np.divide(ETF_averaged, basket_averaged))
plt.xscale('log')
plt.xlabel('value of ETF fund')
plt.ylabel('fund value ratio ETF/basket')

fig1.savefig('fund_value_vs_time.png')
fig2.savefig('growth_rate_vs_time.png')
fig3.savefig('growth_rate_vs_fund_value.png')
fig4.savefig('ETF_and_basket_final_distribution.png')
fig5.savefig('fund_value_ration_ETF_basket_vs_time.png')
fig6.savefig('fund_value_ration_ETF_basket_vs_fund_value.png')