Note: This paper comparing two different kinds of auction systems for IPOs was written for Yale Computer Science 455: Economics & Computation. The first and only auction-based IPO was for Google in 2004. Despite Google's established brand even in 2004, it priced at the low end of estimates and still increased 17% on the first day, which is not what one would expect with an auction designed to establish the best price at the IPO. Since then, companies have been more worried about completing a successful IPO than trying to pioneer new pricing techniques. Maybe Facebook will use an auction-based IPO to ensure that all users have access to stock at the IPO, not just the best customers of the lead underwriter.
An Initial Public Offering, or IPO, is the financial event where a previously privately-held company sells common stock and is then listed on a major stock exchange. There are two principal reasons why a company enters the IPO market: to provide liquidity for the founders, venture capitalists, and employees with existing stock holdings; and to obtain new funds for the the company's use[6].
Traditionally, a company will hire an investment banking firm, such as Goldman Sachs or Morgan Stanley, to lead the promotion and underwriting of an IPO. After the U.S. Security and Exchange Commission (SEC) has approved the offer, the issuing company and its investment banker agree on a price and quantity for the firm's IPO shares (with an additional overallotment provision that gives the investment banker the option to sell as many as 15% more shares)[5]. After a price has been set, shares cannot be sold at a higher price even if the number of shares demanded at the offering price greatly exceeds the agreed-upon quantity[5]. Issuing IPO shares under this system is very expensive for the company, with a total cost of 7% of the gross proceeds going to underwriter commissions; legal, printing, and auditing expenses; and other out-of-pocket costs[5].
However, because the price of the IPO shares is fixed, the company cannot receive more proceeds even if the demand was greatly underestimated. Even in the average case, IPO shares appreciate more than 15% from the issue price to the first trade on the stock exchange[6]. This raises the question whether an auction for IPO shares could yield more profit for the company while also offering other advantages intrinsic to certain kinds of auctions. The traditional IPO mechanism violates the Consumer Sovereignty principal of incentive-compatible, deterministic auctions because an investor with the highest valuation who bids that valuation will not necessarily receive shares even if her valuation exceeds the offer price[6]. While some countries, such as England, require underwriters to distribute shares impartially, underwriters in the U.S. are allowed to allocate the majority of the shares to their most profitable customers[6].
This traditional system of IPO pricing may change, however, as one company has already created a new auction-based IPO pricing mechanism. In 1999, W.R. Hambrecht & Co introduced OpenIPO, an IPO pricing mechanism based on the incentive-compatible k-item Vickrey auction. That auction sells k items to the top k bidders, where each bidder pays a price equal to the bid value of bidder k+1[7]. It is also the only sealed-bid, single-round auction for k identical items where bidding one's valuation is incentive-compatible[1].
While the k-item Vickrey auction offers incentive compatibility, Fiat et al. note that there are situations where the auctioneer could obtain more revenue by selling fewer than k items[1]. Because IPOs are often postponed when the demand for a company's shares appears insufficient, they consider a subset of truthful auctions where the auctioneer can cancel the auction while still offering incentive-compatibility. They then propose the Sampling Cost Sharing (SCS) Auction as an example of a cancellable auction, and prove that it can produce an expected profit of between 1/4 and 1/2 of an optimal profit-maximizing auction for a restricted class of input data[1]
It is impossible to precisely compare the traditional IPO pricing mechanism against standard auction types, as that mechanism is based as much on an investment banker's intuition as on any consistent algorithm. Instead, I will compare the incentive-compatible (but not constant competitive) k-item Vickrey auction used by the OpenIPO mechanism, the incentive-compatible and constant competitive SCS auction, and the optimal (but not incentive-compatible) single-price auction.
To evaluate these three auctions, the Microsoft SQL Server database system was programmed to generate data, run auctions, and compare results. The methods used to generate data are explained in the next section, and the procedures themselves are included in the Appendix.
Rock's theory suggests that there are two types of bid distributions for IPO shares: "hot" issues will have two superimposed bid distributions, one each for the informed and uninformed investors; "cold" issues will have only one bid distribution, as the informed investors will shun the issue. Note that in the case of "hot" issues, it is enough to assume that there are two bid distributions, without requiring that the bids for uninformed investors be distributed around a lower price than the bids of the informed investors. While it is also possible that there are three or more points around which groups of bids are distributed, as the number of these points becomes arbitrarily large, the resultant distribution of all bids can be approximated by a distribution of bids around a single point. For simplicity, only bids with distributions around one and two points will be considered.
The class of normal distributions, whose curve is given by the
equation
will be used to determine the number of bids y (each for 1,000 shares) at the valuation x[3]. Four different values (0.25, 0.625, 1.0, 1.375) for the standard deviation σ will be used to yield four output bid distributions for the single-point normal distribution. (Note that the mean μ is this single point.) Normally, a mean μ=0 would reflect a normal distribution centered around the origin. For these experiments, the mean has been re-centered to $50. Figure 1 shows overlaid graphs of the four single-point output bid distributions.
In the two-point case, each point is the mean μ of a normal distribution curve. The values of both points, which correspond to two valuations, determine the offset of the two curves, and therefore affect the output bid distribution. The four offset values that will be used are: $5, $10, $20, and $30. All 16 combinations of these values will be used for each value of σ, yielding a total of 64 output bid distributions for the two-point normal distribution.
A random distribution of bid quantities and valuations will be generated by scaling the [0,1] output range of the Microsoft SQL Server RAND() function. Four values (10, 20, 32, and 64) will be used for the scaling factor for the the bid valuation for each random interval (bid quantities will have a fixed scaling value), and four values ($5, $10, $20, and $30) will be used for the offset distance between the start of the two random intervals. Because the bid quantities have a fixed scaling value, the order of the two intervals in the two-point case is irrelevant. Thus the total number of output bid distributions produced for the two-point case is 40, while four output bid distributions are produced for the one-point case. See Figure 2 for an illustration of a two-point random bid distribution.
Figure 2: Two-point random bid distribution with a scaling factor of 64 applied to both random distributions and an offset distance of $30 between the start of the two random intervals.
Figure 3: The revenue and price calculated by the SCS and k-item mechanisms as compared with the revenue and price of the optimal auction.
As the table of auction results in Appendix B shows, the average revenue obtained by the SCS auction was 50.2% of the optimal auction's revenue, while the price was 99.7% of the optimal auction's price. The average revenue of the k-item Vickrey auction was 68.2% of the optimal auction's revenue, while the price was 135.3% of the optimal auction's price. Thus, the price and revenue of the k-item Vickrey auction are 35% higher than the SCS auction for the same number of shares sold. These results suggest that the average-case revenue obtained by the SCS auction will be about one-half of the revenue of the optimal single-price auction, while the k-item Vickrey auction will obtain approximately two-thirds of the optimal auction revenue.
The SCS auction does not offer good revenue-maximizing properties in the average case, considering that the k-item Vickrey auction obtains approximately one-third more revenue for the same number of items sold. However, there is a reason why companies should, if given the choice, choose the SCS auction over a k-item Vickrey auction. One of the main criticisms of the k-item Vickrey-auction-based OpenIPO mechanism is that it removes the unmet demand that is preset in a traditional IPO. The same can be said for the optimal auction: by satisfying all of the bidders with the highest valuations, only the bidders with lower valuations will remain. Thus, few investors will be willing to buy the stock at a higher price than the IPO price after an optimal or k-item Vickrey auction. In contrast, the SCS auction would guarantee a significant amount of unmet demand by allocating shares to only half of the highest bidders. Thus, a company could expect that a significant number of the high bidders who did not receive shares in the IPO would buy shares on the exchange if the stock's price did not rise dramatically. This residual demand at the IPO price will provide a significant level of support under the stock, reducing the likelihood that it will fall below the IPO price onces it begins trading on an exchange.
Although the revenue that a company could collect with the SCS auction would be half that of the optimal auction, a company with a strong stock can easily conduct a secondary public offering within a year of the IPO. Thus, if the SCS auction contributes to the strength of the stock by insuring unmet demand, then this concern over IPO revenue may prove to be myopic.
While the SCS auction itself does not satisfy the principle of Consumer Sovereignty, neither does the traditional IPO mechanism. However, because IPO bidders can always purchase the stock on the exchange (albeit at a higher price) if they do not receive an allocation in the IPO, Consumer Sovereignty is satisfied when the IPO and exchange-trading are considered together.
CREATE PROCEDURE GenNormalDist @IPOID tinyint, @valSD float, @valMean float AS
/* For the 'two-point' case, this function is called twice with the same
* @IPOID value but different @valSD and @valMean values
* NOTE: valMean should be called with 1/10 the intended value */
DECLARE @Valuation float, @x float, @numBidders float, @expVal float
SET @x = 0
WHILE @x < 10
BEGIN
SET @numBidders = 0; SET @expVal = 0;
/* determine the exponent for e (Note Valuation is shifted right by 5)*/
SELECT @expVal = - POWER(@x - 5 - @valMean, 2)/(2*POWER(@valSD, 2))
/* calculate the number of 1000 share bidders at this point */
SELECT @numBidders = (100/(@valSD * 2.506)) * POWER(2.71828, @expVal)
SET @Valuation = 1000 * @x
WHILE @numBidders > 0.5
BEGIN
INSERT INTO tblBidders (IPOID, Valuation, QuantityDemanded)
VALUES (@IPOID, CAST(@Valuation AS smallint), 10 )
SET @numBidders = @numBidders - 1
END
SET @x = @x + 0.1
END
CREATE PROCEDURE GenRandomDist @IPOID tinyint, @valScale smallint, @valStart smallint AS
/* For the 'two-point' case, this function is called twice with @valStart values
* that differ by the random interval offset value */
DECLARE @i int, @Valuation float, @QuantityDemanded float
SET @i = 0
WHILE @i < 1000
BEGIN
/* valuation will range from from @valStart to @valStart + @valScale */
SELECT @Valuation = @valScale * RAND() + @valStart
/* quantity will range from 1 (100 shares) to 255 (25,500 shares) */
SELECT @QuantityDemanded = 255 * RAND()
INSERT INTO tblBidders (IPOID, Valuation, QuantityDemanded)
VALUES (@IPOID, CAST(@Valuation AS smallint), CAST(@QuantityDemanded as tinyint) )
SET @i = @i + 1
END
CREATE PROCEDURE SCSAuction @IPOID tinyint AS
DECLARE @totalRevenue int, @lastValuation smallint, @partitionVal BIT, @thisRand float
DECLARE @userValuation smallint, @bidderID int, @lastRand float, @quantityDemanded tinyint
DECLARE @totalSold int
DECLARE valList CURSOR LOCAL STATIC FOR
SELECT BidderID FROM tblBidders WHERE IPOID=@IPOID
OPEN valList
SET @lastRand = 0; SET @thisRand = 0
FETCH FROM valList INTO @bidderID
WHILE @@FETCH_STATUS = 0
BEGIN
SET @thisRand = RAND()
IF ( @thisRand > @lastRand )
UPDATE tblBidders SET SCSPartition = 1 WHERE BidderID=@bidderID
ELSE
UPDATE tblBidders SET SCSPartition = 0 WHERE BidderID=@bidderID
SET @lastRand = @thisRand
FETCH NEXT FROM valList INTO @bidderid
END
DEALLOCATE valList
SET @totalRevenue = 0; SET @lastValuation = 0; SET @partitionVal = 0; SET @totalSold = 0
EXEC SCSOptimalAuction @IPOID, @partitionVal, @lastValuation OUTPUT, @totalRevenue OUTPUT
IF @totalRevenue = 0 /* no winners, so run SCSOptimalAuction on the other partition */
BEGIN
SET @partitionVal = 1
EXEC SCSOptimalAuction @IPOID, @partitionVal, @lastValuation OUTPUT, @totalRevenue OUTPUT
END
UPDATE tblBidders SET SCSAllocation = 0 WHERE IPOID=@IPOID /* clear existing allocation */
UPDATE tblBidders SET SCSAllocation=QuantityDemanded
WHERE SCSPartition = @partitionVal AND IPOID=@IPOID AND Valuation >= @lastValuation
SELECT @totalSold = SUM(SCSAllocation) FROM tblBidders
WHERE SCSPartition = @partitionVal AND IPOID=@IPOID
INSERT INTO tblAuctionResults (IPOID, AuctionType, SettlePrice, QuantitySold, TotalRevenue)
VALUES (@IPOID, 3, @lastValuation, @totalSold, @totalRevenue)
CREATE PROCEDURE SCSOptimalAuction
@IPOID tinyint, @partitionVal BIT, @lastValuation smallint OUTPUT, @totalRevenue int OUTPUT
AS
DECLARE @valuation smallint, @quantity tinyint, @auctionType tinyint, @totalQuantity int
/* create a temporary table for the quantity available at each valuation */
CREATE TABLE #tmpTable (Price smallint, TotalRevenue int, TotalSold int)
INSERT INTO #tmpTable
SELECT DISTINCT(Valuation) AS Price, 0 AS TotalRevenue, 0 AS TotalSold
FROM tblBidders WHERE IPOID = @IPOID AND SCSPartition = @partitionVal
DECLARE valList CURSOR LOCAL STATIC FOR
SELECT Valuation, QuantityDemanded FROM tblBidders
WHERE IPOID=@IPOID AND SCSPartition = @partitionVal
ORDER BY Valuation DESC
OPEN valList
SET @totalRevenue= 0; SET @lastValuation = 0; SET @totalQuantity = 0
FETCH FROM valList INTO @valuation, @quantity
WHILE @@FETCH_STATUS = 0
BEGIN
/* this user is willing to buy at any price less than or equal to her valuation */
UPDATE #tmpTable SET TotalSold = TotalSold + @quantity
WHERE Price <= @valuation
FETCH NEXT FROM valList INTO @valuation, @quantity
END
UPDATE #tmpTable SET TotalRevenue = TotalSold * CAST(Price as int)
/* Now select the max revenue */
SELECT TOP 1 @lastValuation = Price, @totalRevenue = TotalRevenue, @totalQuantity = TotalSold
FROM #tmpTable ORDER BY TotalRevenue DESC
DEALLOCATE valList
DROP TABLE #tmpTable
CREATE PROCEDURE OptimalAuction @IPOID tinyint AS
DECLARE @valuation smallint, @quantity tinyint
DECLARE @price smallint, @totalRevenue int, @totalQuantity int
/* create a temporary table for the quantity available at each valuation */
CREATE TABLE #tmpTable (Price smallint, TotalRevenue int, TotalSold int)
INSERT INTO #tmpTable
SELECT DISTINCT(Valuation) AS Price, 0 AS TotalRevenue, 0 AS TotalSold
FROM tblBidders WHERE IPOID = @IPOID
DECLARE valList CURSOR LOCAL STATIC FOR
SELECT Valuation, QuantityDemanded FROM tblBidders WHERE IPOID=@IPOID
ORDER BY Valuation DESC
OPEN valList
SET @totalRevenue = 0; SET @price = 0; SET @totalQuantity = 0
FETCH FROM valList INTO @valuation, @quantity
WHILE @@FETCH_STATUS = 0
BEGIN
/* user is willing to buy at any price less than or equal to her valuation */
UPDATE #tmpTable SET TotalSold = TotalSold + @quantity
WHERE Price <= @valuation
FETCH NEXT FROM valList INTO @valuation, @quantity
END
UPDATE #tmpTable SET TotalRevenue = TotalSold * CAST(Price as int)
/* Now select the max revenue */
SELECT TOP 1 @price = Price, @totalRevenue = TotalRevenue, @totalQuantity=TotalSold
FROM #tmpTable ORDER BY TotalRevenue DESC
UPDATE tblBidders SET OptimalAllocation = QuantityDemanded
WHERE Valuation >= @price AND IPOID=@IPOID
INSERT INTO tblAuctionResults (IPOID, AuctionType, SettlePrice, QuantitySold, TotalRevenue)
VALUES (@IPOID, 2, @price, @totalQuantity, @totalRevenue)
DEALLOCATE valList
DROP TABLE #tmpTable
CREATE PROCEDURE OpenIPO @IPOID tinyint, @k INT AS
DECLARE @valuation smallint, @quantity int, @fillRatio float
DECLARE @price smallint, @totalRevenue int, @totalQuantity int
DECLARE valList CURSOR LOCAL STATIC FOR
SELECT DISTINCT(Valuation) AS Valuation, SUM(CAST(QuantityDemanded as int)) AS Quantity
FROM tblBidders WHERE IPOID = @IPOID GROUP BY Valuation, QuantityDemanded
ORDER BY Valuation DESC
OPEN valList
SET @totalRevenue = 0; SET @price = 0; SET @totalQuantity = 0
FETCH FROM valList INTO @valuation, @quantity
WHILE @@FETCH_STATUS = 0
BEGIN
SET @price = @valuation /* in case there are no more rows */
IF (@totalQuantity + @quantity < @k) /* all bids at this valuation get allocated */
BEGIN
UPDATE tblBidders SET VickreyAllocation=QuantityDemanded WHERE Valuation = @valuation
SET @totalQuantity = @totalQuantity + @quantity
END
ELSE /* some bids at this valuation will get allocated */
BEGIN
SET @fillRatio = CAST((@k - @totalQuantity) as float)/(CAST(@quantity as float))
UPDATE tblBidders SET VickreyAllocation = CAST( (QuantityDemanded * @fillRatio) as tinyint)
WHERE Valuation = @valuation
SET @totalQuantity = @totalQuantity + CAST((@quantity * @fillRatio) as int)
/* now get the k+1st price */
FETCH NEXT FROM valList INTO @valuation, @quantity
IF @@FETCH_STATUS = 0
BEGIN
SET @price = @valuation /* the k+1st price */
BREAK
END /* if @@FETCH_STATUS != 0, we can use the existing @Price value */
END
FETCH NEXT FROM valList INTO @valuation, @quantity
END
SET @totalRevenue = @totalQuantity * @price
INSERT INTO tblAuctionResults (IPOID, AuctionType, SettlePrice, QuantitySold, TotalRevenue)
VALUES (@IPOID, 1, @price, @totalQuantity, @totalRevenue)
DEALLOCATE valList