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.

 

  1. Holden says:
    (link)

    Honestly I think this whole exercise is a little bit of a waste of time. In my opinion the psychological factors dominate mathematical ordering, bid speed, and any other easy to model factors except for valuation.

  2. 7-Year Grich says:
    (link)

    Sorry if you answered this last week, but what does speed have to do with it? I’ve never been at an auction where my quickness getting to the bid button cost me a player… am I totally misunderstanding this exercise?

  3. Kevin says:
    (link)

    Sorry if this was covered in an introductory article.

    But, should action values change with every player off the board?
    With Cash from owners and Players out of ‘universe’ (Expected/Available points per position)

    IE. maybe a bad example, but assume that Catchers ranked from 2-10 are all selected and a minority of cash has been spent so far, shouldn’t catcher ranked #1 value be higher (maybe marginally) now assuming difference between #1 and #11 is great and ignoring Catcher playing time/C strategy/etc

    • Different Paul says:
      (link)

      @Kevin: In a “perfect” auction, no. Every player is priced exactly correctly, so the value (performance/cost) for Posey exactly the same as #11.

      In a real auction it could go either way. Let’s say 12 team 1 catcher league. It could be that the 3 guys remaining undervalue catcher (they haven’t selected one yet), so he goes for cheap. On the other hand, if they value him “fairly”, there may be marginal inflation.

      If Posey is first off the board, you’re balancing his value against your predicted maximum value for all the other catchers. If you think, say, Perez is undervalued in projections, you might decline a $1 discount on Posey because you think you can get a $3 discount on Perez. With 3 catchers left, there’s a smaller pool so it’s less likely you’re predicting a discount remains in the pool, so it makes more sense to pay market price.

      An asymmetry exists, in that you’re looking for the maximum value guy. If your predicted sleeper goes at market, you’re now willing to pay a little more for other guys. On the other hand, if a bust goes for cheap, you’re less likely to tune down your predictions because you were essentially ignoring that player. That said, a strong auction player will understand this asymmetry and curb his optimism when evaluating early players.

      • Kevin says:
        (link)

        @Different Paul:

        I understand, and maybe I’m just looking at it from a different way and doing a bad job explaining. I’m trying to simulate that mid round inflation. Think mid-range save guys when everyone realizes that they’ve got decent amount of cash left but not too many guys with SV opportunity left. Sometimes overpaying is justified, even though its higher than the predraft valuation, because middraft there are so many points & cash available.

        • Different Paul says:
          (link)

          @Kevin:

          Ah, got you. I have definitely seen that as well, where last player on a tier is elevated.

          I’ve also seen the opposite, where a binge of early spending leads to mid round deflation.

          I suppose it’s a function of player values being nonlinear: e.g., after I draft a C in a 1 C league, the value of other catchers plummets for me. By the same token, the value of your first 0+30 saves is different than 100+30 saves…interesting stuff

    • paul

      paul says:
      (link)

      @Kevin: Kevin,

      First off, thanks for the improvements on my code. Will take the time to understand what it’s doing and implement the changes in future versions.

      Yes, I think the auction values should be dynamic in an auction, but I do believe adjustments are minor except in extreme/obvious cases (one closer left, you can’t fit another 3Bman on your roster). So it’s further down on my list of features to incorporate in the program.

  4. Different Paul says:
    (link)

    I’ve never been in an auction with a 3 second bid timer, I don’t think a freeze bid’s primary effect is that the other owners can’t make a decision fast enough.

    My opinion is that it immediately sets a player in “fair value” territory, and suggests some degree of interest on the bidders part, such that others are unlikely to get him at +$1, but will have to bid up towards market price. If nobody is excited about the player, they might just all let him slip by.

    On the other hand, if you bid 1/2 value and there’s a lull, players will think “I’d take him for that!” or “no way he’s getting that good a deal” and jump in. And once you’re bidding for a guy, it’s easy for your brain to start rationalizing why you’re pursuing him and get a little ahead of itself.

    I think a more realistic simulation might be to have players elect at the start of an bidding if they’re “interested” in this guy. If there’s a 5 second gap between bids they re-evaluate, based on a function of the gap between his current price and their projected value. So if nobody is interested in Trout, they all suddenly reconsider when he’s going for $1. But if a $20 guy is going for $18 they stick with there guns. That also becomes a function you can tune to simulate “in on every bidding” players, “price enforcers” and “the easily distracted”

  5. jiveballer says:
    (link)

    Seems that this topic would be better served through the study of actual live auction data rather than attempting to apply unsophisticated AI in a simulation.

Comments are closed.