Visualizing Historical Food Prices in Units of Fiat, Gold, or Stock

library(tidyverse)
library(lubridate)
library(glue)
library(scales)
library(tidyquant)
library(rvest)
library(readxl)

I recently had an interesting discussion about the impact of the gold standard on food price stability. The gold standard has existed in the US since 1792 in some form or another, with only a few interruptions. The US government nationalized gold in 1934 and kept the value of the US dollar tied to gold reserves. The Bretton Woods system effectively maintained the gold standard until 1971. Afterwards, gold prices fluctuated freely and were not tied to the US dollar anymore.

So how did the end of Bretton woods impact food price volatility? Moreover, how would have food prices have evolved in a hypothetical world where we had to pay in ounces of gold rather than fiat currency? These questions call for a simple visualization that compares the evolution of food price indexes with different numeraires. In this post, I consider 3 different types of numeraires: fiat currency (US dollar), ounces of gold, and stocks (units of the S&P 500 total return index) - where the latter serves as a benchmark asset that was not literally tied to the other two. I focus on the US simply because of data availability.

Gold prices

First, I need to get annual gold prices in US dollar. Fortunately, FRED offers daily gold fixing prices in London Bullion Markets since 1968 where I can easily get the last price for each year.

tbl.GoldFRED <- tq_get("GOLDPMGBD228NLBM", get="economic.data",
                   from = "1968-01-01",to = "2021-02-01") %>%
  na.omit() %>%
  group_by(Year = year(date)) %>%
  arrange(desc(date)) %>%
  filter(row_number()==1) %>%
  ungroup() %>%
  select(Year, GoldUSD = price) %>% 
  arrange(Year)

Gold prices before 1968 can be scrapped from onlygold.com. The website contains annual gold prices since 1792, but unfortunately the information is contained in a horrible table format. The code below extracts the annual gold prices and arranges them in a desireable long format.

tbl.GoldScrapped <- read_html("https://onlygold.com/gold-prices/historical-gold-prices/")  %>% 
  html_table(fill = TRUE) %>% 
  .[[1]] %>% as_tibble()

tbl.GoldFREDPre <- bind_rows(
  tbl.GoldScrapped %>% 
  transmute(Year = as.integer(X1),
            GoldUSD = parse_number(X2)),
  tbl.GoldScrapped %>% 
    transmute(Year = as.integer(X4),
              GoldUSD = parse_number(X5)),
  tbl.GoldScrapped[, c(7, 8)] %>% 
    transmute(Year = as.integer(X7),
           GoldUSD = parse_number(X8))) %>% 
  na.omit() %>% 
  arrange(Year) %>% 
  filter(Year >= 1928)

So let’s put the two data sources together and move on to the next time series.

tbl.GoldAnnual <-  bind_rows(
  tbl.GoldFRED, filter(tbl.GoldFREDPre, Year < min(tbl.GoldFRED$Year))
) %>% 
  arrange(Year)

S&P total return index

As the second alternative numeraire, I want to have the S&P 500 total return index which I fortunately already reconstructed historically in this blog post. Importantly, the total return index tracks both capital gains and cash distributions and represents the overall performance of the S&P 500 to shareholders.

tbl.SP500Monthly <- read_rds("data/SP500Monthly.rds")

tbl.SP500Annual <- tbl.SP500Monthly %>%
  group_by(Year = year(Month)) %>% 
  arrange(Month) %>% 
  summarize(SP500PerformanceUSD = last(TotalReturnIndex)) %>% 
  filter(Year >= 1928) 

Food prices

Lastly, I get the US-dollar-denominated annual food price index based on consumer price for all urban consumers from FRED.

tbl.FoodAnnual <- tq_get("CPIUFDNS", get = "economic.data",
                         from = "1928-01-01",to = "2020-12-31") %>%
  na.omit() %>%
  group_by(Year = year(date)) %>%
  arrange(desc(date)) %>%
  filter(row_number()==1) %>%
  ungroup() %>%
  select(Year, FoodIndexUSD = price) %>% 
  arrange(Year)

During and after Bretton Woods

Now, I combine the different data into a single table and compute food indexes with gold and S&P total return index as numeraires, respectively. To ensure comparability I normalize each time series by its Value in the year 1928. I hence get different time series that all start in 1928.

tbl.DataAnnual <- tbl.FoodAnnual %>% 
  full_join(tbl.SP500Annual, by = "Year") %>% 
  full_join(tbl.GoldAnnual, by = "Year") %>% 
  mutate(FoodIndexGold = FoodIndexUSD / GoldUSD,
         FoodIndexSP500 = FoodIndexUSD / SP500PerformanceUSD) %>% 
  arrange(Year) %>% 
  pivot_longer(cols = -Year, names_to = "Type", values_to = "Value") %>% 
  group_by(Type) %>% 
  arrange(Year) %>% 
  mutate(Value = Value / first(Value) * 100,
         Change = Value / lag(Value) -1) %>% 
  ungroup() 

Let us look the evolution of the different annual indexes with US dollar as the denominator. I use log scale, otherwise I wouldn’t be able to see anything other than the astounding cumulative performance of the S&P 500 total return index. The figure also shows that gold price volatility increased substantially after Bretton Woods, while food prices grew rather linearly.

fig.IndexesEvolution <- tbl.DataAnnual %>% 
  filter(Type %in% c("FoodIndexUSD", "SP500PerformanceUSD", "GoldUSD"))  %>% 
  mutate(Type = case_when(Type == "FoodIndexUSD" ~ "Food",
                          Type == "GoldUSD" ~ "Gold",
                          Type == "SP500PerformanceUSD" ~ "S&P 500 Total Return")) %>% 
  ggplot(aes(x = Year, y = Value, color = Type)) +
  geom_line() +
  scale_y_log10(labels = comma) +
  geom_vline(xintercept = 1971, linetype = "dashed") +
  geom_text(aes(x = 1971, y = 250000, label = "End of Bretton Woods system"), color = "black", hjust = -0.1) + 
  labs(x = NULL, y = NULL, color = "Index",
       title = "Evolution of different annual indexes in US dollar (1928 = 100)")+
  theme_bw()
fig.IndexesEvolution

Next, I look at the evolution of food price indexes using US dollar, ounces of gold or units of the S&P 500 total return index as numeraires. The figure shows that food became increasingly expensive in US dollar terms over time. Not surprisingly, the raise in gold prices would have rendered food substantially less expensive in gold terms. However, the price of food in S&P total return index units nearly approached zero.

fig.NumerairesEvolution <- tbl.DataAnnual %>% 
  filter(Type %in% c("FoodIndexUSD", "FoodIndexGold", "FoodIndexSP500")) %>% 
  mutate(Type = case_when(Type == "FoodIndexUSD" ~ "US Dollar",
                          Type == "FoodIndexGold" ~ "Gold",
                          Type == "FoodIndexSP500" ~ "S&P 500 Total Return"),
         Type = factor(Type, levels = c("US Dollar", "Gold", "S&P 500 Total Return"))) %>% 
  ggplot(aes(x = Year, y = Value, color = Type)) +
  geom_line() +
  scale_y_log10(labels = comma) +
  geom_vline(xintercept = 1971, linetype = "dashed") +
  geom_text(aes(x = 1971, y = 10000, label = "End of Bretton Woods system"), color = "black", hjust = -0.1) + 
  labs(x = NULL, y = NULL, color = "Numeraire",
       title = "Evolution of food price indexes with different numeraires (1928 = 100)")+
  theme_bw()
fig.NumerairesEvolution

In the following visualization, I want to plot the average annual food prices changes in different periods: during the Bretton woods system, the 10 years after Bretton woods and since 1981. I compute the geometric mean which is appropriate in situations where values are meant to be multiplied together or that are exponential in nature. I just tock the function from here:

fun.geometric_mean = function(x, na.rm = TRUE){
  x <- 1+x
  out <- exp(sum(log(x[x > 0]), na.rm = na.rm) / length(x)) - 1
  return(out)
}

I first construct a summary table by period before I plot the statistics.

tbl.DataAnnualSummarized <- tbl.DataAnnual  %>% 
  filter(Type %in% c("FoodIndexUSD", "FoodIndexGold", "FoodIndexSP500")) %>% 
  mutate(Period = cut(Year, c(-Inf, 1970, 1980, Inf), 
                      c("Bretton Woods system", "1971-1980", "Since 1981"))) %>% 
  mutate(Type = case_when(Type == "FoodIndexUSD" ~ "US Dollar",
                          Type == "FoodIndexGold" ~ "Gold",
                          Type == "FoodIndexSP500" ~ "S&P 500 Total Return"),
         Type = factor(Type, levels = c("US Dollar", "Gold", "S&P 500 Total Return"))) %>% 
  na.omit() %>% 
  group_by(Period, Type) %>% 
  summarize(N = n(),
            ChangeMean = fun.geometric_mean(Change),
            ChangeSD = sd(Change))

The figure below now shows the (geometric) average food index changes with different numeraires. It demonstrates that the price of food in US dollar terms increased strongly in the first 10 years after Bretton Woods, but reached similar growth levels to the Bretton Woods system afterwards. The deflationary effect of gold seems to be concentrated in the first 10 years after Bretton Woods, afterwards it is relatively weak. The S&P total return index would have lead to food price deflation in all periods, but was particularly weak in the first 10 years after Bretton Woods.

fig.ComparisonMean <-  tbl.DataAnnualSummarized %>% 
  ggplot(aes(y = fct_rev(Type), x = ChangeMean, fill = fct_rev(Period))) +
  geom_col(position = "dodge") +
  theme_bw() + 
  scale_x_continuous(labels = percent) + 
  labs(x = NULL, y = NULL, fill = "Period",
       title = "Geometric mean of annual food index changes with different numeraires") +
  guides(fill = guide_legend(reverse = TRUE))
fig.ComparisonMean

The last figure shows the standard deviation of annual food index changes with different numeraires. It illustrates how the price of food became less volatile in US dollar terms after Bretton Woods, whereas it would have been much more volatile in gold or S&P 500 units in the same time period. The volatility of food in ounces of gold was particularly voaltile in the first 10 years after the end of Bretton Woods.

fig.ComparisonSD <-  tbl.DataAnnualSummarized %>% 
  ggplot(aes(y = fct_rev(Type), x = ChangeSD, fill = fct_rev(Period))) +
  geom_col(position = "dodge") +
  theme_bw() + 
  scale_x_continuous(labels = percent) + 
  labs(x = NULL, y = NULL, fill = "Period",
       title = "Standard deviation of annual food index changes with different numeraires") +
  guides(fill = guide_legend(reverse = TRUE))
fig.ComparisonSD

Of course the visualizations above suggest some counter-factual scenarios that are far from reality. Probably the world would have looked very differently if US citizens had to pay for food in ounces of gold or shares of the S&P 500 total return index. Nonetheless, simple visualizations are able to tell interesting stories.

Christoph Scheuch
Christoph Scheuch
Director of Product

My interests include Product Management, Data Science, R and FinTech-related stuff. matter.

Related