Chapter 8: The Exposure Problem - The Danger of Partial Winning
Imagine you are trying to build a railway connecting City A to City B. You need to buy land in Segment 1 AND Segment 2.
If you get both, you make $1,000,000 profit.
If you get only one, you make $0 profit (a half-built railway is useless).
This is called Complementarity (or Synergy).
The Danger: Selling Items Separately¶
In a “Simultaneous Ascending Auction,” Segment 1 and Segment 2 are sold in separate auction rooms at the same time.
You bid on both. You are winning both at $100k each. Great!
Suddenly, a competitor bids $900k for Segment 1.
The Trap:
You can’t beat $900k (Total cost would be $1M, leaving zero profit).
So you stop bidding on Segment 1.
BUT: You are still the high bidder on Segment 2 for $100k.
Result: You lose Segment 1, win Segment 2. You pay $100k for a piece of land you can’t use. You lose $100k.
This fear causes bidders to be conservative, leading to inefficient markets. Let’s simulate this “train wreck” scenario.
import time
def run_exposure_simulation():
"""
Simulates a Simultaneous Ascending Auction (SAA)
Scenario:
- Bidder 1 (Synergy): Wants Item A + Item B. Value: $100. Value of single item: $0.
- Bidder 2 (Local A): Wants only Item A. Value: $70.
- Bidder 3 (Local B): Wants only Item B. Value: $20.
"""
print("--- SIMULATION STARTING ---")
print("Items for Sale: A, B")
print("Bidder 1 (Synergy): Val({A,B})=$100, Val({A})=$0, Val({B})=$0")
print("Bidder 2 (Local): Val({A})=$70")
print("Bidder 3 (Local): Val({B})=$20")
print("-" * 40)
# Current High Bids
prices = {'A': 0, 'B': 0}
winners = {'A': None, 'B': None}
# Bidding Limits (Budget)
limit_b1 = 100 # Total for A+B
limit_b2 = 70 # For A
limit_b3 = 20 # For B
# Simulation Step (Price Increment $10)
increment = 10
for round_num in range(1, 15):
print(f"Round {round_num}: Prices A=${prices['A']}, B=${prices['B']}")
bids_placed = False
# --- BIDDER 2 LOGIC (Local A) ---
# If not winning A and price is low enough, bid.
if winners['A'] != 'Bidder 2' and prices['A'] + increment <= limit_b2:
prices['A'] += increment
winners['A'] = 'Bidder 2'
print(f" -> Bidder 2 bids ${prices['A']} on A")
bids_placed = True
# --- BIDDER 3 LOGIC (Local B) ---
# If not winning B and price is low enough, bid.
if winners['B'] != 'Bidder 3' and prices['B'] + increment <= limit_b3:
prices['B'] += increment
winners['B'] = 'Bidder 3'
print(f" -> Bidder 3 bids ${prices['B']} on B")
bids_placed = True
# --- BIDDER 1 LOGIC (Synergy) ---
# This is the tricky part.
# Current exposure: cost of items currently winning.
# Future cost: cost to beat current prices.
cost_to_win_A = prices['A'] if winners['A'] == 'Bidder 1' else prices['A'] + increment
cost_to_win_B = prices['B'] if winners['B'] == 'Bidder 1' else prices['B'] + increment
total_cost = cost_to_win_A + cost_to_win_B
# Strategy: Aggressive. Try to win BOTH if total cost < $100.
if total_cost <= limit_b1:
if winners['A'] != 'Bidder 1':
prices['A'] += increment
winners['A'] = 'Bidder 1'
print(f" -> Bidder 1 bids ${prices['A']} on A (Trying to get package)")
bids_placed = True
if winners['B'] != 'Bidder 1':
prices['B'] += increment
winners['B'] = 'Bidder 1'
print(f" -> Bidder 1 bids ${prices['B']} on B (Trying to get package)")
bids_placed = True
else:
# STOP BIDDING. The package is too expensive (> $100).
# CRITICAL MOMENT: Check if Bidder 1 is stuck with just one item.
print(f" -> Bidder 1 STOPS. Total Package Cost ${total_cost} > Limit $100")
if not bids_placed:
print("No new bids. Auction Ends.")
break
time.sleep(0.5)
print("...")
print("-" * 40)
print("--- FINAL RESULT ---")
print(f"Winner A: {winners['A']} at ${prices['A']}")
print(f"Winner B: {winners['B']} at ${prices['B']}")
# Calculate Bidder 1's Outcome
b1_items = []
b1_cost = 0
if winners['A'] == 'Bidder 1':
b1_items.append('A')
b1_cost += prices['A']
if winners['B'] == 'Bidder 1':
b1_items.append('B')
b1_cost += prices['B']
print(f"\nBidder 1 Outcome: Won {b1_items} for ${b1_cost}")
if len(b1_items) == 2:
print("Result: SUCCESS! Profit = $100 - Cost")
elif len(b1_items) == 0:
print("Result: SAFE. Won nothing, paid nothing.")
else:
print("Result: DISASTER (EXPOSURE)! Paid for 1 item, but Value is $0.")
print(f"Net Loss: -${b1_cost}")
run_exposure_simulation()--- SIMULATION STARTING ---
Items for Sale: A, B
Bidder 1 (Synergy): Val({A,B})=$100, Val({A})=$0, Val({B})=$0
Bidder 2 (Local): Val({A})=$70
Bidder 3 (Local): Val({B})=$20
----------------------------------------
Round 1: Prices A=$0, B=$0
-> Bidder 2 bids $10 on A
-> Bidder 3 bids $10 on B
-> Bidder 1 bids $20 on A (Trying to get package)
-> Bidder 1 bids $20 on B (Trying to get package)
...
Round 2: Prices A=$20, B=$20
-> Bidder 2 bids $30 on A
-> Bidder 1 bids $40 on A (Trying to get package)
...
Round 3: Prices A=$40, B=$20
-> Bidder 2 bids $50 on A
-> Bidder 1 bids $60 on A (Trying to get package)
...
Round 4: Prices A=$60, B=$20
-> Bidder 2 bids $70 on A
-> Bidder 1 bids $80 on A (Trying to get package)
...
Round 5: Prices A=$80, B=$20
No new bids. Auction Ends.
----------------------------------------
--- FINAL RESULT ---
Winner A: Bidder 1 at $80
Winner B: Bidder 1 at $20
Bidder 1 Outcome: Won ['A', 'B'] for $100
Result: SUCCESS! Profit = $100 - Cost
Analysis: The Tragedy of the “Global” Bidder¶
In this simulation, Bidder 1 (the Synergy bidder) likely faced a disaster.
They drove the price of Item A up to compete with Bidder 2.
Eventually, the price of A + B exceeded $100.
Bidder 1 had to stop bidding.
Result: Bidder 2 wins A (High Value), but Bidder 1 is left holding the bag for Item B. They paid money for half a railroad!
The Fix: Package Bidding This is why modern auctions allow Package Bids (Combinatorial Auctions).
Instead of bidding on “A” and “B” separately, you submit a bid: “I pay $100 for {A, B} combined. Or $0 for anything else.”
The auctioneer acts as a computer, checking if your $100 package bid beats the sum of the individual bids ($70 + $20 = $90).
In this case, Package Bidding would allow Bidder 1 to win safely and efficiently.