Note: We’ve got real baseball games being played and I’m stuck in this theoretical auction world. If you’re not prepared to dive into coding and stat minutiae, maybe save your energies for my article next week. Fair warning…
If you were with us last time, I introduced a program that simulated an auction given a list of the participants, a list of the players to be bid on, and lists of how much each owner valued each player. The reason I built this program was to learn more about auction strategy. The issue with my program was, even though it simulated auctions well enough, it was hard to glean any meaningful information from the results.
And that’s essentially where I left off last time. The main reason it was difficult to learn anything from it was that I assigned, essentially, an arbitrary speed for each agent to bid at.
The Hare’s bid time was randomly chosen from 1 to 8.
Paul’s bid time was randomly chosen between 1 to 9.
The tortoise’s bid time was randomly chosen between 1 and 10.
This was good for making one agent faster and another slower, but if you asked me what the meaning of the 8 9 and 10 were, the answer was… nothing.
How do we get meaningful bid times? A general theme of this project will be that things will “become meaningful” when they accurately represent how a real auction works. In this case, bid times represent an interval of time, probably best measured in seconds.
In auctions, a very important interval of time is 3 seconds. Although the auctioneer in the Geico commercial doesn’t say it, most say some form of “Ten dollars going once… going twice… ten dollars sold!” Roughly speaking, if no bid is placed within three seconds of the last bid, the bidding is ovah. Right now, my program only stops the bidding when only one agent wants to bid. Let’s change that.
To code this into the program, a very simple check is performed to see if the minimum Bid Time is greater than 3. If true, the bidding stops and the player is sold at the current price.
Updated code can be found here.
I even added some dialogue that mimics a virtual auctioneer announcing the player being sold. Let’s see what it looks like:
pauls-air-3:pyth paulsingman$ python discreteauc.py
The current bid is: 1
The highest bidder is: paul
**********
Bid times: {‘paul’: 100, ‘tortoise’: 5.274753978, ‘hare’: 2.7055799407}
The current bid is: 2
The highest bidder is: hare
**********
Bid times: {‘paul’: 1.5100168639, ‘tortoise’: 2.494432927, ‘hare’: 100}
The current bid is: 3
The highest bidder is: paul
**********
Bid times: {‘paul’: 100, ‘tortoise’: 4.51590362, ‘hare’: 1.6586592901}
The current bid is: 4
The highest bidder is: hare
**********
Bid times: {‘paul’: 3.1079105985, ‘tortoise’: 3.7063709913, ‘hare’: 100}
Mike Trout for 4 going once, 4 dollars going twice… sold! to hare
The results of the auction are: [[(‘hare’, 4)]]
(I like the concept of adding more dialogue to the output. I’ll keep adding new lines each week.)
In this run of the program. There were once again three agents bidding on one player, whom they each valued at 10. Instead of reaching 10, however, the bidding stopped at 4 because afterwards neither Paul nor the Tortoise submitted their bid in under three seconds. The Tortoise came in at 3.7 seconds and Paul was just a hair too late at 3.1 seconds.
Instead of making their bid times randomly chosen from 1 to 8, 9, and 10, for this run their bid times were chosen uniformly with right endpoints of 4, 5, and 6. Still arbitrary, but at least now they reflect something tangible, seconds to bid.
Testing Freeze Bids
Enough theoretical nonsense. Let’s test what Rudy mentioned in the comments last week. He said “I like making 3/4 retail price bids when I throw out a player. In the CBSSports AL auction, I got Michael Saunders and Brandon Moss by just being active (i know for Saunders I did a 3/4 bid).”
This 3/4 opening bid Rudy’s talking about is what we in the business call a Freeze Bid. Instead of starting the bidding at $1, you start it at $8 for a player you’d pay up to $12 for, in hopes of catching the other owners off guard. Sometimes it works, sometimes it doesn’t. The concept at least, makes sense.
To test this in the program we will do the following. Each agent will have a specified interval of possible bid times, say anywhere from 1 to 3 seconds. If a Freeze Bid is placed, additional seconds are added to each owner’s subsequent bid time, say it will now be between 2 and 4 seconds.
Converting from Bid Times to Probabilities (and back)
I don’t know about you, but I’m tired of talking in terms of bid times; It’s not intuitive. What I do have some intuitive sense of is probabilities. Instead of saying an owner’s bid time range increases from 1-3 to 2-4 if a Freeze Bid is made, let’s say an owner gets his bid in successfully 95% under normal circumstances, and only 60% of the time after a Freeze Bid is made.
If you disagree with these percentages, share your thoughts and I can always adjust the parameters.
Now, we have these percentages in mind, but I can’t just type into the program “If there’s no Freeze Bid, The Hare will bid in under 3 seconds 95 percent of the time”. To encode it in the program, I have to convert the probability back to a Bid Time range.
To do this we’ll travel back to Stat 101 class and look up the cumulative distribution function (CDF) of the uniform distribution. The CDF gives us what the probability is of a randomly chosen value from the interval from a to b being less than a given x. For the uniform distribution the equation is:
Probability = (x – a)/(b – a)
For this scenario we have x = 3, a =1 , b = the upper bound of the range we’re solving for, and the Probability = 95% or .95. So,
.95 = (3 – 1)/(b – 1)
b = 3.1053
In other words, if we want the agent’s bid time to be less than three 95% of the time, his Bid Time range should be from 1 to 3.1053.
To solve what the range should be with a Freeze Bid:
.60 = (3 – 1)/(b – 1)
b = 4.333
It makes sense that if we want the bid time to be greater than 3 more often, the upper bound should be higher.
Admitting We Have A Problem
I’m all ready to code the new Bid Times in the program and set it running… but I just realized we have a small problem.
I already know what the answer will be. It’s just a simple stat problem.
With three agents (meaning two are bidding at each round) and no freeze bid made, the chance of the bidding stopping early is (.05)^2 = 0.25% If we say each person values the player at 12, then the bidding could stop at each point along the way from 1 to 12, so the total probably of the player going for less than $12 is (12-1)*.0025, or 2.75%.
If a freeze bid is used and you start the bidding at $8, the chance of you winning the player for that amount is .40^2 = 16%. There are then another three opportunities for the player to go for less than $12 under normal bidding conditions, adding another 3*(.05)^2 = 0.75% chance the player is won for less than retail value.
The simulator shouldn’t tell us anything different, but I suppose it’s worth running a thousand or so times to see if agrees.
Without any freeze bidding, we expect the bidding to stop early 27.5 times out of 1000. Running the model 1000 times gives the follow results:
pauls-air-3:pyth paulsingman$ python freeze.py
The bidding stopped early 26 times
The player went for retail price 974 times
Woo! 26 is quite close to 27.5. Running it a few more times, that number does jump around a fair amount. I’ve seen it go as low as 18 times the bidding stopped early, but never above 30. I’m not going to even begin to try to calculate what the variance of these 1000 trials should be.
Now let’s test some freeze bids. Again, we expect the freeze to work 16% of the time, or 160 times out of 1000.
pauls-air-3:pyth paulsingman$ python freeze.py
The freeze worked 160 times
The player went for retail price 840 times
I know it’s not as climactic for you not running the program, but still try to empathize with me getting excited about seeing 160 exactly. Let me run that a few more times and see how stable that number is. Two more trials and I got 162 and 154, so that is fairly consistent it seems.
Conclusion and Discussion
I’ll admit, I’m fairly disappointed this ended up not being a good test for the simulator. But, if you followed along, hopefully you learned something along the way. That’s the whole point of going into some of the grittier details I get into.
Adding freeze bid functionality does bring us one step closer to simulating all aspects of a real auction, so I can’t call this a pointless exercise for my purposes.
I’ll leave you this week with some topics I consider open to discussion:
- Don’t like my use of the uniform distribution in the bid times? I chose it cause it keeps things relatively simple, the math easy, and I don’t think it’s a terrible approximation of people’s bidding behavior. If you think otherwise, what would you use instead?
- What should the percentages be for how much a freeze bid decreases the chances of another owner making their bid? Ideally I’d get data from a real auction and keep track of the freeze bids, but that’s near impossible to get a large enough sample on so we’re left guessing.
Time for sleep. Til next time, folks.