There was news recently that UK inflation has dropped from 3.4% to 3.2%, and that this is reason to celebrate.
We will be using the following data:
We will investigate how inflation actually affects the people of the UK and whether there is reason to celebrate.
We need to get the data into a format we can actually use it.
We initially deleted all but the per-month data, producing a format like so:
0001 import csv 0002 0003 inflate_file = open("uk-inflation/series-170424.csv", mode="r") 0004 inflate_reader = csv.reader(inflate_file) # Parse CSV 0005 0006 def get_row(reader, num) : 0007 for row in reader: 0008 num -= 1 0009 if num <= 0 : return row 0010 return None 0011 0012 print(str(get_row(inflate_reader, 1))) # Print first line 0013 inflate_file.seek(0) # Return to start of file
['1989 JAN', '5.7']
The first thing we should do is parse the line:
0014 def month_to_num(month_name) : 0015 month = None 0016 if month_name.startswith("jan") : month = 1 0017 elif month_name.startswith("feb") : month = 2 0018 elif month_name.startswith("mar") : month = 3 0019 elif month_name.startswith("apr") : month = 4 0020 elif month_name.startswith("may") : month = 5 0021 elif month_name.startswith("jun") : month = 6 0022 elif month_name.startswith("jul") : month = 7 0023 elif month_name.startswith("aug") : month = 8 0024 elif month_name.startswith("sep") : month = 9 0025 elif month_name.startswith("oct") : month = 10 0026 elif month_name.startswith("nov") : month = 11 0027 elif month_name.startswith("dec") : month = 12 0028 else : month = -1 0029 return month 0030 0031 def inflate_parse_line(row): 0032 if len(row) != 2 : 0033 print("Incorrect number of columns in row: " + str(len(row))) 0034 return (None, None, None) 0035 date_raw = row[0].split(" ") 0036 if len(date_raw) != 2 : 0037 print("Incorrect parts of date: " + str(len(date_raw))) 0038 return (None, None, None) 0039 # Convert year 0040 year = int(date_raw[0]) 0041 # Convert month 0042 month_name = date_raw[1].lower() 0043 month = month_to_num(month_name) 0044 # Convert inflation 0045 inflation = float(row[1]) 0046 return (year, month, inflation) 0047 0048 year, month, inflation = inflate_parse_line(get_row(inflate_reader, 1)) 0049 inflate_file.seek(0) # Return to start of file 0050 print("Year: " + str(year) + ", \tMonth: " + str(month) + ", \tInflation: " + str(inflation))
Year: 1989, Month: 1, Inflation: 5.7
And now we should be able to replicate the graph showing inflation over time:
0051 plot_raw = Plott( 0052 title = "Inflation and Interest Rates Over Time in the UK", 0053 x_title = "Time (months since January 1989)", 0054 y_title = "Inflation/Interest (percentage %)", 0055 fg = "#FFF", 0056 bg = "#222" 0057 ) 0058 # Collect some stats 0059 min_year = 99999999 0060 max_year = -1 0061 # Setup X and Y axis 0062 win_size = 12 # Months 0063 x = [] 0064 y = [] 0065 z = [] 0066 for row in inflate_reader : 0067 year, month, inflation = inflate_parse_line(row) 0068 if year > max_year : max_year = year 0069 if year < min_year : min_year = year 0070 x += [ ((year - 1989) * 12) + month ] 0071 y += [ inflation ] 0072 if len(y) >= win_size : 0073 t = 0 0074 for i in range(len(x) - win_size, len(y)) : 0075 t += y[i] 0076 z += [ t / win_size ] 0077 else : z += [ inflation ] 0078 plot_raw.plot(x, y, "Raw inflation values") 0079 plot_raw.plot(x, z, "Smoothed inflation values (window = " + str(win_size) + " months)") 0080 inflate_file.seek(0) # Reset the position into the file 0081 print(plot_raw.to_uri()) # Display the graph
Very import note: We assume that interest rates for saving are approximately the same as interest rates on borrowed money. In fact, it’s worse. The recent loan rates have not been passed onto bank savers.
As done previously, we need to read and parse the CSV data:
0082 import csv 0083 0084 interest_file = open("uk-inflation/boe-interest.csv", mode="r") 0085 interest_reader = csv.reader(interest_file) # Parse CSV 0086 0087 print(str(get_row(interest_reader, 1))) # Print first line 0088 interest_file.seek(0) # Return to start of file
['03 Aug 23', '5.25']
And each row needs to be parsed into a format we can use:
0089 def interest_parse_line(row): 0090 if len(row) != 2 : 0091 print("Incorrect number of columns in row: " + str(len(row))) 0092 return (None, None, None) 0093 date_raw = row[0].split(" ") 0094 if len(date_raw) != 3 : 0095 print("Incorrect parts of date: " + str(len(date_raw))) 0096 return (None, None, None) 0097 # Convert year 0098 year = int(date_raw[2]) 0099 # Note: Terrible reporting format by the BoE. 0100 if year < 50 : year += 2000 0101 else : year + 1900 0102 # Convert month 0103 month_name = date_raw[1].lower() 0104 month = month_to_num(month_name) 0105 # Convert inflation 0106 interest = float(row[1]) 0107 return (year, month, interest) 0108 0109 year, month, interest = interest_parse_line(get_row(interest_reader, 1)) 0110 interest_file.seek(0) # Return to start of file 0111 print("Year: " + str(year) + ", \tMonth: " + str(month) + ", \tInterest: " + str(interest))
Year: 2023, Month: 8, Interest: 5.25
Now we need to perform additional processing, as the data is not really as periodic as the ONS data. For a given month and year we want to estimate the interest rate.
0112 def get_interest_rate(reader, year, month) : 0113 date = ((year - 1989) * 12) + month 0114 just_below = [ date - 99999999, None, None, None ] 0115 just_above = [ date + 99999999, None, None, None ] 0116 for row in interest_reader : 0117 y, m, i = interest_parse_line(row) 0118 d = ((y - 1989) * 12) + m 0119 if d > date and d < just_above[0] : 0120 just_above = [ d, y, m, i ] 0121 if d < date and d > just_below[0] : 0122 just_below = [ d, y, m, i ] 0123 if just_above[3] != None : 0124 return (just_below[3] + just_above[3]) / 2 0125 else : 0126 return just_below[3] 0127 0128 interest = get_interest_rate(interest_reader, 2022, 1) # Doesn't exist 0129 interest_file.seek(0) # Return to start of file 0130 print("Year: " + str(2022) + ", \tMonth: " + str(1) + ", \tInterest: " + str(interest))
Year: 2022, Month: 1, Interest: 0.375
Note: I am aware that this is not ideal and that there is data missing, but it is good enough to serve the point.
Now let us update our data:
0131 # Setup X and Y axis 0132 win_size = 12 # Months 0133 i = [] 0134 for row in inflate_reader : 0135 year, month, inflation = inflate_parse_line(row) 0136 interest = get_interest_rate(interest_reader, year, month) 0137 interest_file.seek(0) # Return to start of file 0138 i += [ interest ] 0139 plot_raw.plot(x, i, "Interest values (interpolated)") 0140 inflate_file.seek(0) # Reset the position into the file 0141 print(plot_raw.to_uri()) # Display the graph
Note: We don’t smooth the interest values because the dataset is already sparse, and it is not clear if smoothing the data creates for a noise-reduced picture.
The thing about inflation is that it never goes away. This was something that was difficult to communicate, and why I ultimately ended up writing this article.
Let’s say you are a humble person, and you saved £10,000 since 2010 (after the 2008 financial recession)and put it in the bank.
0142 savings = 10000
Now to answer the question: How much is £10,000 worth now compared to £10,000 in the current market?
0143 plot_save = Plott( 0144 title = "Devaluation of Money in the UK", 0145 x_title = "Time (months since January 2010)", 0146 y_title = "Money (GBP £)", 0147 fg = "#FFF", 0148 bg = "#222" 0149 ) 0150 # Setup X and Y axis 0151 x = [] # Date 0152 save = [] 0153 zero = [] 0154 y_carry = float(savings) 0155 y = [] # Accumalitive (de-)inflation 0156 z_carry = float(savings) 0157 z = [] # Accumalitive interest 0158 q_carry = float(savings) 0159 q = [] # Accumalitive combined 0160 for row in inflate_reader : 0161 year, month, inflation = inflate_parse_line(row) 0162 if year < 2010 : continue # Only since 2010 0163 x += [ ((year - 1989) * 12) + month ] 0164 save += [ savings ] 0165 zero += [ 0 ] 0166 # NOTE: Inflation is per annum, so must divide by 12 months. 0167 y_carry *= 100.0 / (100.0 + (inflation / 12)) 0168 y += [ int(y_carry) ] 0169 interest = get_interest_rate(interest_reader, year, month) 0170 interest_file.seek(0) # Return to start of file 0171 z_carry *= (100.0 + (interest / 12)) / 100.0 0172 z += [ int(z_carry) ] 0173 q_carry *= 100.0 / (100.0 + (inflation / 12)) 0174 q_carry *= (100.0 + (interest / 12)) / 100.0 0175 q += [ q_carry ] 0176 plot_save.plot(x, save, "£" + str(savings) + " fixed (0% inflation)") 0177 plot_save.plot(x, zero, "£0 (for context)") 0178 plot_save.plot(x, y, "£" + str(savings) + " at UK inflation") 0179 plot_save.plot(x, z, "£" + str(savings) + " at UK interest") 0180 plot_save.plot(x, q, "£" + str(savings) + " at UK interest under UK inflation (combined)") 0181 inflate_file.seek(0) # Reset the position into the file 0182 print(plot_save.to_uri()) # Display the graph 0183 print("In the time since 2010 (" + str(max_year - 2010) + " years)") 0184 print("Your £" + str(savings) + " since 2010 are now worth £" + str(q[-1])) 0185 eco_growth = (savings / q[-1]) * 100 0186 print("The economy appears to have grown by " + str(eco_growth) + "%") 0187 eco_growth_annual = (eco_growth - 100) / (max_year - 2010) 0188 print("Average annual growth by devaluing your savings: " + str(eco_growth_annual) + "%")
In the time since 2010 (14 years) Your £10000 since 2010 are now worth £7579.712388452662 The economy appears to have grown by 131.93112729758104% Average annual growth by devaluing your savings: 2.2807948069700745%
Even when considering the BoE set interest rates (which you may not be getting), the money sitting in the bank lost 25% of its value in 14 years. In the meantime, it looks as it the economy grew by 30+%!
Let that sink in for a minute.
Inflation is a form of tax for people who save money in the bank.3
By saving in the bank, the bank is allowed to lend out your money, whilst maintaining a reserve. The “bank’s money”, is in fact your money. For you allowing them to invest using you money (stocks, loans, etc), they in turn give you interest, and you in turn can store your money digitally and use it anywhere (in theory).
Where things get shit are as follows: Banks are risk adverse, so they only give you a small amount of the reward, and they handle the risk and additional profits. If for some reason they lose money, the UK government prints new money and increases inflation. In turn, all existing money has less purchasing power. As a result, the banks factor in the risk of inflation, and take further interest from your reward.
No matter the outcome, the following always remains true:
The average inflation rate since 2010 is calculated as:
0189 history = [ 0190 [ 199, 0, 0 ], # Inflation since 1989 0191 [ 200, 0, 0 ], 0192 [ 201, 0, 0 ], 0193 [ 202, 0, 0 ], 0194 ] 0195 for row in inflate_reader : 0196 year, month, inflation = inflate_parse_line(row) 0197 for h in history : 0198 if year // 10 == h[0] : 0199 h[1] += inflation 0200 h[2] += 1 0201 inflate_file.seek(0) # Reset the position into the file 0202 for h in history : 0203 h[1] /= h[2] 0204 print("Average inflation in " + str(h[0] * 10) + ": " + str(h[1]) + "%")
Average inflation in 1990: 3.6241666666666665% Average inflation in 2000: 1.9400000000000002% Average inflation in 2010: 2.0666666666666678% Average inflation in 2020: 4.519607843137256%
As you can see, inflation is on the rise. The incoming recession is far worse than anything since before the 1990s. The 2008 financial crisis is already nothing in comparison.
My suggestion is to invest in something with a >5% return on investment.
I would suggest you consider the following:
I do not have the answer. Anybody claiming to do so is a liar, otherwise why aren’t they rich? If they are rich, how do you know you will also end up rich? Pyramid schemes rely on new buyers for growth… (I’m looking at you crypto.)
Good luck out there.
CPIH Annual Rate 00: All Items 2025=100: https://www.ons.gov.uk/economy/inflationandpriceindices/timeseries/l55o/mm23.↩︎
Official Bank Rate history: https://www.bankofengland.co.uk/boeapps/database/Bank-Rate.asp.↩︎
Note that if you don’t save money in the bank, your prospects are slightly worse, but not by much.↩︎