Report Draft

Future Demand and Price Projections of Critical Minerals: Investment Opportunities Analysis

Executive Summary

This report provides a detailed analysis of the demand and price projections for a selected list of critical minerals, using data sourced from the International Energy Agency (IEA) and Bloomberg Terminal. Our analysis operates under different scenario cases to project future trends in the demand for these critical minerals, juxtaposed with their forecasted prices to uncover lucrative investment opportunities. This study is designed to guide investment decisions within the field of critical minerals, emphasizing minerals with the highest potential for returns based on projected demand surges and price appreciations.

Introduction to Critical Minerals

Critical minerals are essential for the global economy, underpinning key sectors such as renewable energy, electronics, and defense. Their significance is escalating in the wake of the global shift towards sustainable and green technologies. This report focuses on a curated list of these minerals, emphasizing their demand dynamics, price forecasts, and investment potential.

Methodology

This analysis is underpinned by a dual-pronged approach: First, we extracted future demand trends for critical minerals based on scenario analysis from the IEA’s database, reflecting different pathways for global energy transition and technology adoption. Second, it utilized Bloomberg Terminal to gather historical and forecasted price data for these minerals. The synthesis of demand projections and price forecasts through visual analysis forms the core of our findings.

library(tidyverse) # pretty much always...
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl) # for reading excel files
library(here) # for using relative file paths
here() starts at /Users/hahazhou/Desktop/Critical Minerals
path <- here("data-raw", "CM_Forecast Price.xlsx")

path
[1] "/Users/hahazhou/Desktop/Critical Minerals/data-raw/CM_Forecast Price.xlsx"
Price <- read_excel("/Users/hahazhou/Desktop/Critical Minerals/data-raw/CM_Forecast Price.xlsx")
New names:
• `` -> `...1`
• `` -> `...2`
• `` -> `...3`
• `` -> `...4`
Price <- na.omit(Price)

Price
# A tibble: 17 × 8
       ...1     ...2     ...3     ...4   `2024`   `2025`   `2026`  `2027`
      <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>    <dbl>   <dbl>
 1  2025     2050     2078.    2100     2042.    2050     1900     1807  
 2    23.5     24.5     25       25.9     24.2     25.4     25       24.2
 3   950      988.    1050     1082.     999.    1200     1250     1372  
 4  1050     1100     1110     1100     1130     1100     1190     1135  
 5  8390     8500     8750     9000     8610     9039.    9250     9539  
 6 16700    16405    16767.   17100    16906.   17900    19180    20250  
 7  2500     2473.    2522.    2590     2525     2662.    2700     2756. 
 8  2150     2100     2100     2180     2116.    2148.    2130     2211  
 9 25500    24902.   24756    25237.   25300.   26900.   29250    30875  
10  2228.    2281.    2313.    2400     2315.    2450     2567.    2745. 
11   930      880      860      840      885      810      770      799  
12   125      115      110      110      116.     103       90       93  
13     4.35     4.35     4.35     4.35     4.53     4.74     4.96     5.5
14 13250    12594.   12694.   14000    13838.   15750    17500    18000  
15    15.5     15       14.8     14.9     14.8     15.9     16.9     19.0
16  4000     4164     5000     4000     4500     5000     5000     5132. 
17    99.1    103.      94.5     91       85       80       70       60  
mineral_names <- c("Gold", "Silver", "Platinum", "Palladium", "Copper", "Nickel", "Zinc", "Lead", "Tin", "Aluminum", "Steel", "Iron", "Manganese", "Lithium", "Cobalt", "Rhodium", "Uranium")

Price <- data.frame(Mineral = mineral_names, Price)
Price
     Mineral     ...1      ...2     ...3     ...4     X2024     X2025     X2026
1       Gold  2025.00  2050.000  2077.50  2100.00  2042.500  2050.000  1900.000
2     Silver    23.50    24.530    25.00    25.90    24.250    25.400    25.000
3   Platinum   950.00   987.500  1050.00  1082.50   998.750  1200.000  1250.000
4  Palladium  1050.00  1100.000  1110.00  1100.00  1130.000  1100.000  1190.000
5     Copper  8390.00  8500.000  8750.00  9000.00  8610.000  9038.940  9250.000
6     Nickel 16700.00 16405.000 16767.05 17100.00 16906.500 17900.000 19180.000
7       Zinc  2500.00  2472.820  2522.50  2590.00  2525.000  2662.500  2700.000
8       Lead  2150.00  2100.000  2100.00  2180.00  2115.500  2147.500  2130.000
9        Tin 25500.00 24902.500 24756.00 25236.99 25300.170 26899.935 29250.000
10  Aluminum  2227.50  2281.220  2313.40  2400.00  2314.925  2450.000  2567.440
11     Steel   930.00   880.000   860.00   840.00   885.000   810.000   770.000
12      Iron   125.00   115.000   110.00   110.00   116.250   103.000    90.000
13 Manganese     4.35     4.350     4.35     4.35     4.525     4.735     4.965
14   Lithium 13250.00 12593.975 12694.27 14000.00 13838.360 15750.000 17500.000
15    Cobalt    15.50    15.000    14.80    14.90    14.800    15.880    16.890
16   Rhodium  4000.00  4164.000  5000.00  4000.00  4500.000  5000.000  5000.000
17   Uranium    99.10   102.725    94.50    91.00    85.000    80.000    70.000
       X2027
1   1807.000
2     24.250
3   1372.000
4   1135.000
5   9539.000
6  20250.000
7   2755.780
8   2211.000
9  30875.000
10  2744.785
11   799.000
12    93.000
13     5.500
14 18000.000
15    18.990
16  5132.500
17    60.000
colnames(Price)[2:5] <- c("2024 Q1", "2024 Q2", "2024 Q3", "2024 Q4")
colnames(Price)[6:9] <- c("2024", "2025", "2026", "2027")
#remove quarterly data
Price <- Price[ , -c(2:5)]
Price
     Mineral      2024      2025      2026      2027
1       Gold  2042.500  2050.000  1900.000  1807.000
2     Silver    24.250    25.400    25.000    24.250
3   Platinum   998.750  1200.000  1250.000  1372.000
4  Palladium  1130.000  1100.000  1190.000  1135.000
5     Copper  8610.000  9038.940  9250.000  9539.000
6     Nickel 16906.500 17900.000 19180.000 20250.000
7       Zinc  2525.000  2662.500  2700.000  2755.780
8       Lead  2115.500  2147.500  2130.000  2211.000
9        Tin 25300.170 26899.935 29250.000 30875.000
10  Aluminum  2314.925  2450.000  2567.440  2744.785
11     Steel   885.000   810.000   770.000   799.000
12      Iron   116.250   103.000    90.000    93.000
13 Manganese     4.525     4.735     4.965     5.500
14   Lithium 13838.360 15750.000 17500.000 18000.000
15    Cobalt    14.800    15.880    16.890    18.990
16   Rhodium  4500.000  5000.000  5000.000  5132.500
17   Uranium    85.000    80.000    70.000    60.000
Price_long <- Price %>%
  pivot_longer(
    cols = starts_with("202"),  # This selects all columns that start with '202'
    names_to = "Year",          # The name of the new column for years
    values_to = "Price"         # The name of the new column for prices
  )
Price_long
# A tibble: 68 × 3
   Mineral  Year   Price
   <chr>    <chr>  <dbl>
 1 Gold     2024  2042. 
 2 Gold     2025  2050  
 3 Gold     2026  1900  
 4 Gold     2027  1807  
 5 Silver   2024    24.2
 6 Silver   2025    25.4
 7 Silver   2026    25  
 8 Silver   2027    24.2
 9 Platinum 2024   999. 
10 Platinum 2025  1200  
# ℹ 58 more rows
ggplot(Price_long) +
  aes(
    x = Year,
    fill = Mineral,
    group = Mineral,
    weight = Price
  ) +
  geom_bar() +
  scale_fill_hue(direction = 1) +
  labs(
    x = "Year",
    y = "Price ($)",
    title = "Forecasted Price for Critical Minerals from 2024 to 2027",
    caption = "Data Source: Bloomberg"
  ) +
  theme_minimal() +
  facet_wrap(vars(Mineral), scales = "free")

This foretasted price visualization presents a dynamic growth trend analysis of critical minerals over the coming three years. Through a series of bar graphs, it illustrates the anticipated level of change in demand for each mineral, categorized by their respective applications and market potential. This visualization effectively highlights minerals with the highest growth prospects, signifying areas where investment could yield substantial returns. The differentiation in growth levels across minerals underscores the diverse demand trajectories shaped by technological advancements and sector-specific requirements.

ggplot(Price_long) +
  aes(
    x = Year,
    y = Price,
    group = Mineral,
    color = Mineral
  ) +
  geom_line()+
labs(
    x = "Year",
    y = "Price ($)",
    title = "Forecasted Price for Critical Minerals from 2024 to 2027",
    caption = "Data Source: Bloomberg"
  ) +
  theme_minimal()

The second price visualization employs a line graph to juxtapose the price trends of different critical minerals over the same three-year horizon. By plotting each mineral’s price trajectory on a single graph, we elucidate not only the comparative differences in their price movements but also the overarching trends that could influence investment decisions. This comparative analysis reveals how certain minerals exhibit stronger price resilience or volatility, providing insights into potential risk and return profiles for investors.

Investment Opportunities

Drawing on the analysis from the visualizations, this report identifies several key minerals that present notable investment opportunities. These are characterized by robust demand growth, favorable price trends, or a combination of both factors, signaling their potential for high investment returns. (Draft to be further edited)