Sunday, August 2, 2026

US oil shares


Like many individuals I’ve been preserving monitor of the slow-burn gasoline disaster arising from the conflict in Iran, its doable growth to the Crimson Sea, and exacerbation from the continuing conflict between Russia and Ukraine. The latter appears to have reached a stage the place critical harm is being executed to Russia’s refining capability and petro merchandise’ exports.

I’ve now began a web page on this web site with a number of charts on the gasoline disaster that I’m going to be preserving updated.

Shares of crude are happening, however “how a lot is left”?

The motivation for right this moment’s submit was that I’d been seeing many information and opinion articles about how the US crude oil shares—in whole, and within the Strategic Petroleum Reserve particularly—have declined to fashionable file lows. However knowledgeable commentators are reluctant to indicate this decline in relation to absolutely the quantity left, versus comparisons to seasonal or historic lows. That is comprehensible as a result of no-one actually is aware of (or maybe tells) what the actual “tank bottoms” are in a giant advanced system just like the USA. A specific amount of oil, of unsure quantity however definitely within the tens (and presumably a whole bunch) of thousands and thousands of barrels, is required simply to maintain numerous bits of plumbing or storage from seizing up or collapsing.

So it’s laborious to get a chart like this one, and I needed to make it myself:

I’ve obtained no status as an oil knowledgeable to fret about, so I can safely draw these strains at a reasonably arbitrary 24 (or 30) days of refinery throughput, and calculate the time it can take to get to these low ranges “on the present price”. It’s simplistic, but it surely’s an necessary actuality examine to see that there is perhaps about 19 weeks left.

Quick because the shares are getting used up, the USA has a lot of oil obtainable to it. However 19 weeks isn’t that many. Markets are betting that good issues will occur within the weeks or months earlier than the blue line reaches the pink line. Due to that, oil is presently promoting for round $90 per barrel as an alternative of the $110+ that will be justified if folks thought we had misplaced the site visitors via the Strait of Hormuz indefinitely.

Not that I feel 19 weeks is a sensible forecast. Lengthy earlier than US shares reached that pink line, there could be financial and political chaos. Efforts to maintain retail costs artificially low would most likely be overwhelmed and there could be materials demand destruction. There would even be big incentives for the US to present away something to revive Persian Gulf and Crimson Sea transit routes. 19 weeks offers us a timeframe inside which one thing must change.

The worth-add for me from this chart is exhibiting the whole oil obtainable for the US within the occasion this turns into an actual emergency for them; the comparability of that to a significant if arbitrary threshold when it comes to days of refinery cowl; and the calculation of weeks left at present draw-down charges to succeed in that top stress threshold.

Get away the SPR and likewise take a look at gasoline, distillates

The identical information supply has plenty of different detailed data, most of it far too detailed for me, however I’m sufficient in 4 key headline variables which can be extra detailed than “whole shares of crude oil”. On this chart I break down the whole shares into these within the Strategic Petroleum Reserve, and people in additional usually accessed shares. I additionally present the quantities held of two key refined merchandise:

Code

Right here’s the code that grabs the info from the US Power Data Administration and attracts that second plot:

library(tidyverse)
library(readxl)
library(scales)
library(glue)

fn <- "us-petrol-status-weekly.xls"

obtain.file(
  "https://ir.eia.gov/wpsr/psw01.xls",
  fn,
  mode = "wb"
)

us_stocks <- read_excel(fn, sheet = "Knowledge 1", skip = 2) |>
  rename(
    crude = `Weekly U.S. Ending Shares of Crude Oil  (Thousand Barrels)`,
    crude_spr = `Weekly U.S. Ending Shares of Crude Oil in SPR  (Thousand Barrels)`,
    gasoline = `Weekly U.S. Ending Shares of Whole Gasoline  (Thousand Barrels)`,
    diesel = `Weekly U.S. Ending Shares of Distillate Gasoline Oil  (Thousand Barrels)`,
    date = Date
  )

#------------------plotting------------------

eia_caption <- glue("Supply: Power Data Administration (EIA). Accessed {format(Sys.Date(), '%d %B %Y')}")

# an annotation rectangle we will use in a number of charts
war_rect <- annotate(
  "rect",
  xmin = as.Date("2026-02-28"),
  xmax = Inf,
  ymin = -Inf,
  ymax = Inf,
  alpha = 0.5,
  fill = "grey80"
)

# Side plot of whole crude, SPR crude, diesel and gasoline shares
us_stocks |>
  mutate(com_crude = crude - crude_spr) |>
  choose(
    date,
    `Non-SPR crude oil` = com_crude,
    `Crude oil in SPR` = crude_spr,
    `Gasoline` = gasoline,
    `Distillate (largely diesel)` = diesel
  ) |>
  collect(variable, worth, -date) |>
  mutate(variable = fct_reorder(variable, worth)) |>
  ggplot(aes(x = date, y = worth / 1000)) +
  war_rect +
  facet_wrap(~variable, scales = "free_y") +
  geom_line(color = "blue") +
  expand_limits(y = 0) +
  scale_y_continuous(label = comma) +
  labs(
    x = "",
    y = "1000's of barrels",
    title = "US shares of crude oil, gasoline, and distillate gasoline oil (successfully diesel)",
    subtitle = "Displaying each whole crude oil shares (crude) and people within the Strategic Petroleum Reserve (crude_spr)",
    caption = eia_caption
  )

As I facet level, I’ve began utilizing air to format my code. Practically all the time it comes out wanting higher and simpler to learn than my guide formatting, and it’s definitely extra constant. Because of D. Vaughan and L. Henry and the tidyverse.org challenge.

And right here’s the code to attract my extremely polished major plot, the one in every of whole crude oil shares with the annotations. I’ve tried to do that in a method that it’s going to maintain working within the months forward with minimal upkeep, we’ll see how that goes.

refinery_throughput <- 17.3 # as at 24 July, working at 97% of US capability. Day by day refinery use.
plausible_stress <- 30 * refinery_throughput
plausible_high_stress <- 24 * refinery_throughput

crude_growth_summary <- us_stocks |>
  summarise(
    latest_crude = crude[date == max(date)],
    weeks = as.numeric(as.Date(max(date)) - as.Date("2026-04-03")) / 7,
    distinction = (latest_crude -
      crude[date == as.Date("2026-04-03")]) /
      1000,
    ratio = latest_crude / crude[date == as.Date("2026-04-03")],
    # development/decline per week in million barrels e.g. 10m barrels per week:
    difference_rate = distinction / weeks,
    # development/declien price per week:
    growth_rate = 1 - exp(log(ratio) / weeks)
  ) |>
  mutate(
    weeks_at_this_rate = (plausible_stress - latest_crude / 1000) /
      difference_rate
  )

plot_us_stocks <- operate(min_date = "2020-01-01", lab_x_diff = NULL) {
  if (!"Date" %in% class(min_date)) {
    min_date <- as.Date(min_date)
  }

  if (is.null(lab_x_diff)) {
    days_shown <- as.numeric(as.Date(max(us_stocks$date))) -
      as.numeric(min_date)
    lab_x_diff <- days_shown / 50
  }

  lv <- tail(us_stocks, 1)$crude / 1000

  p2 <- us_stocks |>
    filter(date >= min_date) |>
    ggplot(aes(x = date, y = crude / 1000)) +
    war_rect +
    geom_hline(yintercept = plausible_stress, color = "darkred") +
    geom_hline(yintercept = plausible_high_stress, color = "pink") +
    annotate(
      "textual content",
      x = min_date + lab_x_diff,
      y = plausible_stress + 50,
      label = "Illustrative stress threshold:n30 days of refinery cowl",
      color = "darkred",
      hjust = 0
    ) +
    annotate(
      "textual content",
      x = min_date + lab_x_diff,
      y = plausible_high_stress - 30,
      label = "Illustrative excessive stress threshold:n24 days of refinery cowl",
      color = "pink",
      hjust = 0,
      vjust = 1
    ) +
    annotate(
      "textual content",
      x = max(us_stocks$date) - 5e6,
      y = lv,
      label = glue(
        "{spherical(lv)} million barrels;n{spherical(lv / refinery_throughput)} days of canopy"
      ),
      dimension = 2.9,
      hjust = 1,
      vjust = 1,
      color = "blue"
    ) +
    geom_line(color = "blue") +
    expand_limits(y = 0) +
    scale_y_continuous(
      label = comma,
      sec.axis = sec_axis(
        ~ . / refinery_throughput,
        title = "Days of refinery throughput"
      )
    ) +
    labs(
      x = "",
      y = "Tens of millions of barrels",
      title = "U.S. Whole Crude Oil Shares, together with Strategic Petroleum Reserve",
      subtitle = glue(
        "Comparability of current inventories with refinery throughput as at July 2026.
Decline since peak on 3 April 2026 is at {abs(spherical(crude_growth_summary$difference_rate, 1))} million barrels per week; {spherical(crude_growth_summary$weeks_at_this_rate)} weeks from stress threshold at this (hypothetical and linear) price."
      ),
      caption = eia_caption
    ) +
    theme(axis.line.y = element_line(color = "grey50"))

    svglite(
      glue("all-us-crude-from{min_date}.svg"),
      width = 10,
      top = 7
    )
    print(p2)
    dev.off()
}

plot_us_stocks("2000-01-01")
plot_us_stocks("2020-01-01")
plot_us_stocks("2025-01-01")

As a result of I did this plot within the type of a operate, I’ve a few variants of the plot. One beginning in 2020:

And the opposite zoomed proper in to only January 2025 and onwards:

On the entire I desire the plot I began with, exhibiting the state of affairs from 2000 and onwards. I’m making an attempt to get some massive image perspective in spite of everything.

That’s all for right this moment. The photographs on this web page will keep the identical as on the time of writing; however these on the gasoline disaster monitoring web page will probably be up to date every week.



Related Articles

Latest Articles