~ $ cd projects/fish-market && cat README.md
The agent that won the fish auction
Aplicacions d'Intel·ligència Artificial, FIB, autumn 2000. The lab was a competition. Every group wrote a buyer for the same simulated fish market and the marks went by how the buyers did against each other: a 10 for the best, down to about a 4 for the worst. There were two general rounds with every group's agent in the room, the second counting for more. Ours won both — the first with the agent below, the second with the one we handed in two weeks later.
The market is a Dutch auction. A box of fish comes onto the floor, every buyer is told what it resells for, and the auctioneer names a price above that and brings it down. The first buyer to shout takes the box at that price. Nobody bids up; there is nothing to decide but when. And since a price on a box of known resale value is a margin, (value − price) / price, what a buyer decides is the margin below which it will not shout.
Next on the floor: a box of prawns, which resells for 175. The price starts at 263 and falls.
| buyer | asks | holds | spent | worth | credit | profit |
|---|---|---|---|---|---|---|
| Patient | 100% | 0 lots | 0 | 0 | 1029 | 0 |
| Hasty | 5% | 0 lots | 0 | 0 | 1029 | 0 |
| Vicente | 122% | 0 lots | 0 | 0 | 1029 | 0 |
| Wanda | 104% | 0 lots | 0 | 0 | 1029 | 0 |
| Planner | 122% | 0 lots | 0 | 0 | 1029 | 0 |
Press next lot and one box is sold, or withdrawn if nobody wanted it. Run sells them at reading pace, whole morning at once. The board shows what each buyer asks for the box on the floor, what it holds, and what it has made; under it, box by box, who took it at what price and the price every buyer was ready to shout at — which is the whole of each one's mind at that moment. Seat your own agent below the board and it plays too.
The one number
Vicente — the agent was called that — knows two sums: what all the fish still on the floor resells for, and how much credit the buyers have left between them. If that money bought all that fish, every unit spent would return
That is the margin at which the market clears: the price level at which the money in the room and the fish in the room exactly meet. Vicente shouts at that margin and not before. Both sums move as the morning goes — each box sold leaves the first, each price paid leaves the second — and the margin moves with them.
private void recalcularGanancia()
{
ganancia = sumValor - sumCredito;
ganancia /= sumCredito;
}
public void evOffer(Good g, double precio)
{
double valor;
double gananciaActual;
valor = g.getResalePrice();
gananciaActual = valor - precio;
gananciaActual /= precio;
if (gananciaActual <= 0)
return;
if (gananciaActual >= ganancia && canIBid)
bid(precio);
}
That is Vicente.java, less a line that printed. In English, name for name:
private void recomputeMargin()
{
margin = fishLeft - moneyLeft;
margin /= moneyLeft;
}
public void onOffer(Good g, double price)
{
double value;
double marginNow;
value = g.getResalePrice();
marginNow = value - price;
marginNow /= price;
if (marginNow <= 0)
return;
if (marginNow >= margin && canIBid)
bid(price);
}
fishLeft is sumValor, the resale value of what is still on the floor; moneyLeft is sumCredito, the credit in the room counted at 90%. The one dial is eficacia, 0.9: the credit of the others is counted at 90%, on the assumption that they will not manage to spend it all. Less money chasing the same fish means a higher clearing margin, so 0.9 is a measure of greed. The dial above the board is that number. Turn it down and Vicente asks for more and buys less; turn it to 100% and he asks exactly what the room can pay.
Why it wins
Watch a morning with the dial at 50% money. The two naive buyers act first: the hasty one takes box after box at a 5% margin, and the patient one waits for half price and gets it now and then, when nobody else wants a box. For the first twenty or thirty boxes Vicente does nothing. The market's margin is over 100% and no box falls that far while the hasty one has credit — so he does not shout, and he keeps his money.
Then the hasty one is out of credit. Now the money left in the room is mostly Vicente's, the fish left is most of the fish, and the clearing margin tells him so: he can demand twice what the room could have paid an hour ago, and get it, box after box, until his credit and the fish run out together. He does not chase the best box. He buys everything that returns more than the average, and lets the others fight over the rest.
This is not a trick against those two buyers. The clearing margin is where the price would settle if every buyer were rational; a buyer asking more than that goes home with money, a buyer accepting less has overpaid against the room. The agent that stands on the number is the one that cannot be argued down.
What came after it, in seventeen days
The files kept their dates, and there was not one agent but four.
- 1–3 December — Vicente. The clearing margin, alone.
- 2 December — Nulo. A buyer that never shouts. It only watches, and writes down the margin each kind of fish went for. It was the instrument for the next one.
- 10 December — Wanda. The clearing margin, believing the others will spend 98%, corrected as the morning goes: 5% choosier each time she wins a box, 5% less each time a rival who is doing at least as well takes a box at a margin she would have accepted — because somebody is buying under her. She also carried a margin learnt per kind of fish, meant to cap the market's; the one line that chose between the two returned the market's on both branches, so the kinds never spoke. She is seated above as she ran.
- 15 December — the one we handed in. Where Wanda corrected her margin after the fact, this one plans it. It keeps a histogram of the value sold so far by the margin it went at —
p(m), the share of everything sold that went at marginm— and assumes the fish still on the floor will go the same way. Then it sums over every margin it could demand, from the highest down, what the fish it expects to find there would cost, and demands the largestmat which that sum still covers the credit it has left:
That margin is the most it can ask and still spend all its money. Until anything has sold, it is Vicente.
The thread is clean: one global number, then measure, then a number that corrects itself, then a number computed from what was measured and what is left to spend. On the board the three are close, and which of them comes out ahead depends on the morning; the two naive buyers are never in it.
Your own agent
Under the board is an editor with the body of a JavaScript function. It is given lot, the box on the floor; market, with the lots still to sell, everyone's remaining credit and every sale so far; and me, your name in the credits. It returns the margin you demand. It starts out as Vicente in eight lines, so the first agent you seat already holds its own — change the 0.9 first, then try to beat him. A mistake in it is shown in the engine's own words, and the auction goes on without you. It runs in your browser and stays there.
What this is and is not
The auction here is a reconstruction, not the original market: the price falls in steps of 2% of the resale value from 150% of it, a box is withdrawn at 25%, and when two buyers shout at once a coin decides where the original restarted the round higher. The agents are the ones in the files, rule for rule. The two naive buyers are not from the class; they are there to show what the number is for.
Eighteen years later I set a class the same problem turned inside out: a lagoon whose fish breed if left alone, and bots that have to decide how much to take from it.