First draft of math code. No frontend, no tests, haven't even tried to compile it yet
This commit is contained in:
		
							
								
								
									
										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() | ||||
| } | ||||
		Reference in New Issue
	
	Block a user