First draft of math code. No frontend, no tests, haven't even tried to compile it yet
This commit is contained in:
parent
9353442cd8
commit
3153771721
70
auctionsim/auction.go
Normal file
70
auctionsim/auction.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package auctionsim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"slices"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RunAuction simulates a single-bid second-price auction with no reserve price
|
||||||
|
* between all bidders emitted by the provided generator, returning the winning
|
||||||
|
* bidder and how much they paid (the maximum bid of the second-highest bidder).
|
||||||
|
*
|
||||||
|
* To simulate an auction between the first several bidders emitted by some
|
||||||
|
* generator, use CappedBidderGenerator (in generators.go).
|
||||||
|
*/
|
||||||
|
func RunAuction(g BidderGenerator) (float64, Bidder) {
|
||||||
|
highBidder := Bidder{}
|
||||||
|
maxBid := math.Inf(-1)
|
||||||
|
prevBid := math.Inf(-1)
|
||||||
|
|
||||||
|
for b, ok := g.Generate(); ok; b, ok = g.Generate() {
|
||||||
|
if c := b.BidCeiling(); c > maxBid {
|
||||||
|
prevBid = maxBid
|
||||||
|
maxBid = c
|
||||||
|
highBidder = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prevBid, highBidder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RunAuctionVerbosely simulates a single-bid second-price auction with no
|
||||||
|
* reserve price between all bidders emitted by the provided generator,
|
||||||
|
* returning all bidders sorted by the order they dropped out and how much the
|
||||||
|
* winning bidder paid.
|
||||||
|
*
|
||||||
|
* Use CappedBidderGenerator to limit the size of the auction and therefore
|
||||||
|
* the number of bidders on the list.
|
||||||
|
*/
|
||||||
|
func RunAuctionVerbosely(g BidderGenerator) float64, []Bidder {
|
||||||
|
var bidders []Bidder
|
||||||
|
for b, ok := g.Generate(); ok; b, ok = g.Generate() {
|
||||||
|
bidders = append(bidders, b)
|
||||||
|
}
|
||||||
|
slices.SortFunc(bidders, func(a, b Bidder) int {
|
||||||
|
// If both bid ceilings are infinite in the same direction, we get NaN here...
|
||||||
|
d := a.BidCeiling() - b.BidCeiling()
|
||||||
|
if d < 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if d > 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// ...which is fine, since NaN will fail both comparisons and return 0 here, so these
|
||||||
|
// (incomparable) infinities get reported as equal, which is good enough.
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(bidders) <= 1 {
|
||||||
|
// With one or zero bidders, there's no auction. The bidder (if any) gets it for the
|
||||||
|
// reserve price, but there is no reserve price, so the auctioneer pays the sole bidder
|
||||||
|
// an infinite amount of money to go away. Economics!
|
||||||
|
return math.Inf(-1), bidders
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final price is the second highest bidder's limit.
|
||||||
|
return bidders[len(bidders)-2].BidCeiling(), bidders
|
||||||
|
}
|
90
auctionsim/bidder.go
Normal file
90
auctionsim/bidder.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package auctionsim
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Bidder represents one competitor in an auction for some resource. It is
|
||||||
|
* not threadsafe. It is a pure value type.
|
||||||
|
*/
|
||||||
|
type Bidder struct {
|
||||||
|
/**
|
||||||
|
* Value stores the real value of the item being auctioned, to this bidder.
|
||||||
|
* If the bidder won the auction at this price, they would make exactly
|
||||||
|
* zero effective profit. It is the "rational bid" in a perfectly efficient
|
||||||
|
* marketplace.
|
||||||
|
*
|
||||||
|
* A bidder that wants some nonzero profit has a lower value on the item by
|
||||||
|
* the amount of profit they want -- that "minimum payoff" represents an
|
||||||
|
* unspecified cost for the time and effort of doing the project at all.
|
||||||
|
* Other costs for getting value out of the object also decrease its value.
|
||||||
|
*
|
||||||
|
* For example, a city to which hosting an F1 race is worth five billion
|
||||||
|
* dollars, but would spend three and a half billion dollars setting up for
|
||||||
|
* it, one billion dollars running it, and lose one billion dollars of tax
|
||||||
|
* revenue due to business disruption during setup and during the event,
|
||||||
|
* correctly values the race for negative five hundred million dollars. As
|
||||||
|
* demonstrated by this example, a value can be negative.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
Value float64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Irrationality is the amount the bidder is wrong by on their belief about
|
||||||
|
* how much the item being auctioned is worth to them. It can be positive
|
||||||
|
* or negative. If it is positive, the bidder will overbid and potentially
|
||||||
|
* lose money by winning for more than the benefit they get from the item.
|
||||||
|
* If it is negative, the bidder will stop bidding too soon and potentially
|
||||||
|
* fail to win an auction that, had they continued bidding, would have given
|
||||||
|
* them the item at a profitable price.
|
||||||
|
*/
|
||||||
|
Irrationality float64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cash stores the bidder's maximum available cash to bid with (including
|
||||||
|
* any debt, liquid assets, etc. that they are willing to commit to the
|
||||||
|
* auction). It is a hard cap on their maximum bid.
|
||||||
|
*
|
||||||
|
* To simulate an auction without cash limits, set this to positive infinity
|
||||||
|
* for every bidder.
|
||||||
|
*
|
||||||
|
* A bidder's available cash can be negative. For example, a company that
|
||||||
|
* cannot offer a bid to perform a job below a certain price has negative
|
||||||
|
* effective cash on hand repersenting the maximum price they may "pay"
|
||||||
|
* to win the auction -- the inverse of the minimum amount of money they
|
||||||
|
* are allowed to accept as compensation for the obligation being auctioned.
|
||||||
|
*/
|
||||||
|
Cash float64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ValueBelief returns how much the bidder *thinks* the item is worth to them.
|
||||||
|
* The actual value to them is b.Value.
|
||||||
|
func (b Bidder) ValueBelief() float64 {
|
||||||
|
return b.Value + b.Irrationality;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BidCeiling returns the highest bid this bidder would make for the item. This
|
||||||
|
* bid might be negative. A negative winning bid is possible and ordinary; this
|
||||||
|
* represents an auction between vendors competing to offer a service at the
|
||||||
|
* lowest price, for example.
|
||||||
|
*/
|
||||||
|
func (b Bidder) BidCeiling() float64 {
|
||||||
|
if b.ValueBelief() > b.Cash {
|
||||||
|
return b.Cash
|
||||||
|
}
|
||||||
|
return b.ValueBelief()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CashCapped returns whether this bidder *would* make a strictly higher bid
|
||||||
|
* than their calculated ceiling if they had the money to do so, but they don't.
|
||||||
|
*/
|
||||||
|
func (b Bidder) CashCapped() bool {
|
||||||
|
return b.ValueBelief() > b.Cash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IsZero returns whether the given Bidder is indistinguishable from one that was zero-initialized.
|
||||||
|
*/
|
||||||
|
func (b Bidder) IsZero() bool {
|
||||||
|
return b.Value == 0 && b.Irrationality == 0 && b.Cash == 0
|
||||||
|
}
|
133
auctionsim/generators.go
Normal file
133
auctionsim/generators.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package auctionsim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Distribution is any stream of float64s.
|
||||||
|
*/
|
||||||
|
type Distribution interface {
|
||||||
|
/**
|
||||||
|
* Draw returns the next float64 from the distribution and true, or
|
||||||
|
* zero and false if the distribution is exhausted or otherwise unusable.
|
||||||
|
*/
|
||||||
|
Draw() float64, bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BidderGenerator is any stream of bidders.
|
||||||
|
*/
|
||||||
|
type BidderGenerator interface {
|
||||||
|
/**
|
||||||
|
* Generate returns the next Bidder and true, or a zero Bidder and false if
|
||||||
|
* the generator is exhausted or otherwise unusable.
|
||||||
|
*/
|
||||||
|
Generate() Bidder, bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BiddersFromDistributions is a BidderGenerator that draws the bidders' values,
|
||||||
|
* irrationality levels, and cash on hand from the provided distributions.
|
||||||
|
*/
|
||||||
|
type BiddersFromDistributions struct {
|
||||||
|
Values Distribution
|
||||||
|
Irrationalities Distribution
|
||||||
|
Bankrolls Distribution
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate implements BidderGenerator.
|
||||||
|
*/
|
||||||
|
func (b *BiddersFromDistributions) Generate() (Bidder, bool) {
|
||||||
|
value, ok := b.Values.Draw()
|
||||||
|
if !ok {
|
||||||
|
return Bidder{}, false
|
||||||
|
}
|
||||||
|
irrationality, ok := b.Irrationalities.Draw()
|
||||||
|
if !ok {
|
||||||
|
return Bidder{}, false
|
||||||
|
}
|
||||||
|
cash, ok := v.Bankrolls.Draw()
|
||||||
|
if !ok {
|
||||||
|
return Bidder{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return Bidder {
|
||||||
|
Value: value,
|
||||||
|
Irrationality: irrationality,
|
||||||
|
Cash: cash,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NormalDistribution is a Distribution representing the normal distribution
|
||||||
|
* with some specified standard deviation and mean. Multiple instances of
|
||||||
|
* this type can share a math.Rand as long as they are not queried concurrently
|
||||||
|
* (since math.Rand is not threadsafe).
|
||||||
|
*/
|
||||||
|
type NormalDistribution struct {
|
||||||
|
Rand *math.Rand
|
||||||
|
StdDev float64
|
||||||
|
Mean float64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw implements Distribution.
|
||||||
|
*/
|
||||||
|
func (n *NormalDistribution) Draw() (float64, bool) {
|
||||||
|
return n.Rand.NormFloat64() * n.StdDev + n.Mean, true
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConstDistribution is a Distribution that always returns the same value.
|
||||||
|
*/
|
||||||
|
type ConstDistribution float64
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw implements Distribution.
|
||||||
|
*/
|
||||||
|
func (c ConstDistribution) Draw() (float64, bool) {
|
||||||
|
return (float64)c, true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CappedDistribution returns some fixed number of values from a Distribution,
|
||||||
|
* then stops returning values.
|
||||||
|
*/
|
||||||
|
type CappedDistribution struct {
|
||||||
|
D Distribution
|
||||||
|
Lim int64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw implements Distribution.
|
||||||
|
*/
|
||||||
|
func (c *CappedDistribution) Draw() (float64, bool) {
|
||||||
|
if c.Lim <= 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
c.Lim--
|
||||||
|
return c.D.Draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CappedBidderGenerator returns some fixed number of bidders from a
|
||||||
|
* BidderGenerator, then stops returning values.
|
||||||
|
*/
|
||||||
|
type CappedBidderGenerator struct {
|
||||||
|
G BidderGenerator
|
||||||
|
Lim int64
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate implements BidderGenerator.
|
||||||
|
*/
|
||||||
|
func (c *CappedBidderGenerator) Generate() (float64, bool) {
|
||||||
|
if c.Lim <= 0 {
|
||||||
|
return Bidder{}, false
|
||||||
|
}
|
||||||
|
c.Lim--
|
||||||
|
return c.G.Generate()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user