Optimal reserve prices in auctions — Myerson’s revenue-maximising design

bayesian-methods
auction-design
reserve-prices
myerson
Derive Myerson’s optimal reserve price for sealed-bid auctions, implement revenue comparisons across uniform and non-uniform value distributions, and visualize the revenue gains from optimal auction design in R.
Author

Raban Heller

Published

May 8, 2026

Modified

May 8, 2026

Keywords

optimal auction, reserve price, Myerson, revenue equivalence, virtual valuation, auction design

Introduction & motivation

A seller auctioning a single indivisible object faces a fundamental design problem: should they accept any positive bid, or should they set a reserve price below which the object is not sold? Intuitively, a reserve price sacrifices some probability of sale in exchange for higher payments from bidders who do participate. The question is how to set this reserve optimally.

Myerson (1981) answered this question definitively in one of the most celebrated results in mechanism design. His Revenue Equivalence Theorem showed that under standard assumptions (independent private values, risk-neutral bidders, symmetric distributions), all standard auction formats yield the same expected revenue when optimally designed. More strikingly, the optimal mechanism can be characterised through virtual valuations: a transformation of bidder values that accounts for the informational rent extracted by each type. The optimal reserve price is the value at which the virtual valuation equals zero — the seller’s own value of retaining the object.

For the canonical case of \(n\) bidders with values drawn independently and uniformly from \([0,1]\), the optimal reserve price turns out to be \(r^* = 1/2\), regardless of the number of bidders. This is a surprising result: even with 100 competing bidders (where the highest bid will be very close to 1), the seller still optimally refuses to sell below \(1/2\). The intuition is that the reserve price targets the case where only one bidder has a value above the reserve — the marginal case where the reserve actually binds. With more bidders, this event becomes rarer, so the reserve price has less impact on overall revenue but remains optimal at the same level.

This tutorial implements Myerson’s framework in R. We derive the optimal reserve for uniform and non-uniform distributions, simulate auctions with and without reserve prices to verify the theoretical predictions, and visualize how expected revenue varies with the reserve price. The analysis connects to broader themes in auction theory: why sellers use minimum prices in art auctions, why governments set reserve prices in spectrum auctions, and why eBay listings with reserve prices behave differently from those without. Understanding optimal reserves is essential for anyone designing or participating in allocation mechanisms where strategic bidding and private information intersect.

Mathematical formulation

Consider \(n\) symmetric bidders with independent private values \(v_i \sim F\) on \([\underline{v}, \bar{v}]\), where \(F\) has density \(f > 0\). The seller values the object at \(v_0 = 0\).

Virtual valuation. The virtual valuation function maps a bidder’s true value to the effective marginal revenue the seller extracts:

\[ \psi(v) = v - \frac{1 - F(v)}{f(v)} \]

This is analogous to marginal revenue in monopoly pricing: \(1 - F(v)\) is the “demand” (probability that a random bidder’s value exceeds \(v\)), and \(f(v)\) converts from values to types.

Optimal reserve price. The optimal reserve \(r^*\) satisfies \(\psi(r^*) = v_0 = 0\):

\[ r^* - \frac{1 - F(r^*)}{f(r^*)} = 0 \quad \Longrightarrow \quad r^* = \frac{1 - F(r^*)}{f(r^*)} \]

Uniform distribution example. For \(v_i \sim U[0,1]\), we have \(F(v) = v\) and \(f(v) = 1\), so \(\psi(v) = 2v - 1\) and \(r^* = 1/2\).

Expected revenue with reserve. In a second-price auction with reserve \(r\) and \(n\) bidders, expected revenue is:

\[ R(r) = \int_r^1 \left[ v \cdot n(n-1) F(v)^{n-2} f(v) (1 - F(v)) + r \cdot n F(v)^{n-1} f(v) \cdot \mathbf{1}[\text{exactly one } v_i \geq r] \right] dv \]

More compactly, using order statistics, expected revenue equals \(E[\max(r, V_{(n-1:n)}) \cdot \mathbf{1}[V_{(n:n)} \geq r]]\) where \(V_{(k:n)}\) is the \(k\)-th order statistic.

For \(U[0,1]\):

\[ R(r, n) = \frac{n}{n+1} - r^n \left( \frac{n}{n+1} r - r + 1 \right) + r \cdot n r^{n-1}(1 - r) + \text{[remaining terms from order stats]} \]

We compute this numerically via simulation for robustness.

R implementation

# --- Virtual valuation for common distributions ---
virtual_valuation_uniform <- function(v) 2 * v - 1

# For power distribution F(v) = v^alpha on [0,1]
virtual_valuation_power <- function(v, alpha) {
  v - (1 - v^alpha) / (alpha * v^(alpha - 1))
}

# Optimal reserve: solve psi(r) = 0
optimal_reserve_uniform <- 0.5  # analytic

# Power distribution: solve numerically
optimal_reserve_power <- function(alpha) {
  uniroot(function(v) virtual_valuation_power(v, alpha),
          interval = c(0.01, 0.99))$root
}

cat("=== Optimal Reserve Prices ===\n\n")
=== Optimal Reserve Prices ===
cat(sprintf("Uniform [0,1]: r* = %.4f\n", optimal_reserve_uniform))
Uniform [0,1]: r* = 0.5000
for (a in c(0.5, 1, 2, 3)) {
  r_star <- if (a == 1) 0.5 else optimal_reserve_power(a)
  cat(sprintf("Power (alpha=%.1f): r* = %.4f\n", a, r_star))
}
Power (alpha=0.5): r* = 0.4445
Power (alpha=1.0): r* = 0.5000
Power (alpha=2.0): r* = 0.5774
Power (alpha=3.0): r* = 0.6300
# --- Simulate auctions to compute expected revenue ---
set.seed(42)
n_sims <- 50000

simulate_revenue <- function(n_bidders, reserve, dist = "uniform",
                              alpha = 1, n_sims = 50000) {
  revenues <- numeric(n_sims)
  for (i in seq_len(n_sims)) {
    if (dist == "uniform") {
      values <- runif(n_bidders)
    } else {
      values <- runif(n_bidders)^(1 / alpha)  # F(v) = v^alpha, so v = U^(1/alpha)
    }
    eligible <- values[values >= reserve]
    if (length(eligible) == 0) {
      revenues[i] <- 0
    } else if (length(eligible) == 1) {
      revenues[i] <- reserve
    } else {
      revenues[i] <- sort(eligible, decreasing = TRUE)[2]  # second-price
    }
  }
  mean(revenues)
}

cat("\n=== Revenue Comparison (Uniform, n=2) ===\n")

=== Revenue Comparison (Uniform, n=2) ===
rev_no_reserve <- simulate_revenue(2, 0)
rev_opt_reserve <- simulate_revenue(2, 0.5)
cat(sprintf("No reserve:       E[Rev] = %.4f  (theory: 1/3 = 0.3333)\n", rev_no_reserve))
No reserve:       E[Rev] = 0.3336  (theory: 1/3 = 0.3333)
cat(sprintf("Reserve r*=0.5:   E[Rev] = %.4f  (theory: 5/12 = 0.4167)\n", rev_opt_reserve))
Reserve r*=0.5:   E[Rev] = 0.4156  (theory: 5/12 = 0.4167)
cat(sprintf("Revenue gain:     %.1f%%\n", 100 * (rev_opt_reserve - rev_no_reserve) / rev_no_reserve))
Revenue gain:     24.6%
cat("\n=== Revenue Comparison (Uniform, n=5) ===\n")

=== Revenue Comparison (Uniform, n=5) ===
rev_no_5 <- simulate_revenue(5, 0)
rev_opt_5 <- simulate_revenue(5, 0.5)
cat(sprintf("No reserve:       E[Rev] = %.4f  (theory: 2/3 = 0.6667)\n", rev_no_5))
No reserve:       E[Rev] = 0.6669  (theory: 2/3 = 0.6667)
cat(sprintf("Reserve r*=0.5:   E[Rev] = %.4f\n", rev_opt_5))
Reserve r*=0.5:   E[Rev] = 0.6719
cat(sprintf("Revenue gain:     %.1f%%\n", 100 * (rev_opt_5 - rev_no_5) / rev_no_5))
Revenue gain:     0.7%
cat("\n=== Revenue across bidder counts ===\n")

=== Revenue across bidder counts ===
for (n in c(1, 2, 3, 5, 10, 20)) {
  rev0 <- simulate_revenue(n, 0, n_sims = 20000)
  rev_r <- simulate_revenue(n, 0.5, n_sims = 20000)
  cat(sprintf("n=%2d: No reserve=%.4f, r*=0.5: %.4f, gain=%.1f%%\n",
              n, rev0, rev_r, 100 * (rev_r - rev0) / max(rev0, 1e-9)))
}
n= 1: No reserve=0.0000, r*=0.5: 0.2475, gain=24745000000.0%
n= 2: No reserve=0.3338, r*=0.5: 0.4172, gain=25.0%
n= 3: No reserve=0.4986, r*=0.5: 0.5331, gain=6.9%
n= 5: No reserve=0.6676, r*=0.5: 0.6720, gain=0.7%
n=10: No reserve=0.8180, r*=0.5: 0.8179, gain=-0.0%
n=20: No reserve=0.9054, r*=0.5: 0.9052, gain=-0.0%

Static publication-ready figure

reserve_seq <- seq(0, 0.95, by = 0.01)
n_values <- c(1, 2, 3, 5, 10)

rev_data <- expand.grid(reserve = reserve_seq, n_bidders = n_values) |>
  as_tibble() |>
  rowwise() |>
  mutate(revenue = simulate_revenue(n_bidders, reserve, n_sims = 10000)) |>
  ungroup() |>
  mutate(n_label = paste0("n = ", n_bidders),
         n_label = factor(n_label, levels = paste0("n = ", n_values)))

p_static <- ggplot(rev_data, aes(x = reserve, y = revenue, color = n_label)) +
  geom_line(linewidth = 0.9) +
  geom_vline(xintercept = 0.5, linetype = "dashed", color = "grey40", linewidth = 0.5) +
  annotate("text", x = 0.52, y = 0.05, label = "r* = 1/2", hjust = 0,
           color = "grey40", size = 3.5) +
  scale_color_manual(values = okabe_ito[1:5], name = "Bidders") +
  labs(title = "Expected revenue vs reserve price (second-price auction, U[0,1])",
       subtitle = "Optimal reserve r* = 1/2 is independent of the number of bidders",
       x = "Reserve price r", y = "Expected revenue") +
  theme_publication() +
  coord_cartesian(ylim = c(0, 1))

p_static
Figure 1: Figure 1. Expected revenue as a function of reserve price in a second-price auction with uniform values on [0,1]. Each curve corresponds to a different number of bidders (n=1,2,3,5,10). The dashed vertical line marks the optimal reserve r*=1/2, which maximises revenue regardless of n. With few bidders, the reserve price has a large impact on revenue; with many bidders, competition alone drives revenue close to 1, and the reserve provides only a marginal improvement. Okabe-Ito palette.

Interactive figure

rev_data_interactive <- rev_data |>
  mutate(text = paste0("Reserve: ", round(reserve, 2),
                       "\nBidders: ", n_bidders,
                       "\nE[Revenue]: ", round(revenue, 4)))

p_int <- ggplot(rev_data_interactive,
                aes(x = reserve, y = revenue, color = n_label, text = text)) +
  geom_line(linewidth = 0.8) +
  geom_vline(xintercept = 0.5, linetype = "dashed", color = "grey40") +
  scale_color_manual(values = okabe_ito[1:5], name = "Bidders") +
  labs(title = "Expected revenue vs reserve price",
       subtitle = "Hover for details; dashed line = optimal reserve",
       x = "Reserve price r", y = "Expected revenue") +
  theme_publication()

ggplotly(p_int, tooltip = "text") |>
  config(displaylogo = FALSE, modeBarButtonsToRemove = c("select2d", "lasso2d"))
Figure 2

Interpretation

The simulations confirm Myerson’s theoretical prediction: for uniformly distributed values on \([0,1]\), the optimal reserve price is \(r^* = 1/2\) regardless of the number of bidders. The economic intuition runs as follows. A reserve price is essentially a take-it-or-leave-it offer to a single bidder. When there is only one bidder (\(n = 1\)), the auction reduces to monopoly pricing, and the optimal price is \(1/2\) (the monopoly price for a linear demand curve). When there are multiple bidders, competition raises the expected payment above \(r^*\) in most realisations, so the reserve only binds in the marginal case where exactly one bidder values the object above \(r\). The probability of this event shrinks with \(n\), but the optimal threshold remains \(1/2\) because the virtual valuation function — and hence the point where it crosses zero — depends only on the distribution, not on \(n\). The revenue gains from an optimal reserve are most dramatic with few bidders (25% improvement for \(n = 2\), over 100% for \(n = 1\)) and diminish as \(n\) grows, since competition itself drives revenue toward the upper bound of the value distribution. For non-uniform distributions, the optimal reserve shifts: concave distributions (values concentrated near 1) have lower optimal reserves, while convex distributions (values concentrated near 0) have higher ones. This analysis assumes risk-neutral bidders and independent private values; with correlated values or risk-averse bidders, the optimal mechanism may involve different instruments beyond a simple reserve price.

References

Myerson, Roger B. 1981. “Optimal Auction Design.” Mathematics of Operations Research 6 (1): 58–73. https://doi.org/10.1287/moor.6.1.58.
Back to top

Reuse

Citation

BibTeX citation:
@online{heller2026,
  author = {Heller, Raban},
  title = {Optimal Reserve Prices in Auctions — {Myerson’s}
    Revenue-Maximising Design},
  date = {2026-05-08},
  url = {https://r-heller.github.io/equilibria/tutorials/bayesian-methods/auction-optimal-reserve-prices/},
  langid = {en}
}
For attribution, please cite this work as:
Heller, Raban. 2026. “Optimal Reserve Prices in Auctions — Myerson’s Revenue-Maximising Design.” May 8. https://r-heller.github.io/equilibria/tutorials/bayesian-methods/auction-optimal-reserve-prices/.