Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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.

  1. You bid on both. You are winning both at $100k each. Great!

  2. Suddenly, a competitor bids $900k for Segment 1.

  3. 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.

  1. They drove the price of Item A up to compete with Bidder 2.

  2. Eventually, the price of A + B exceeded $100.

  3. Bidder 1 had to stop bidding.

  4. 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.