Auction Winner's-Curse Haircut
Table of Contents
- [Example](#example)
- [Workflow](#workflow)
- [Common Patterns](#common-patterns)
- [Guardrails](#guardrails)
- [Quick Reference](#quick-reference)
Example
Scenario: Bidder has estimated a target's value at rawvaluation = $30. Six informed bidders are competing. Estimates across bidders are moderately dispersed (signaldispersion = 40 out of 100). The target is a well-known commodity (everyone models it similarly).
Inputs:
raw_valuation: 30
valuetype: commonvalue
ninformedbidders: 6
signal_dispersion: 40
Haircut computation:
haircut_pct = min(35, 10 + log(6) x 5 + 40 x 0.2)
= min(35, 10 + 1.792 x 5 + 8)
= min(35, 10 + 8.96 + 8)
= min(35, 26.96)
= 26.96 (clamped below 35 ceiling)
Output:
adjusted_valuation = 30 x (1 - 0.2696) = $21.91
haircut_pct = 26.96
classification_rationale: "Common-value target with 6 informed bidders and moderate signal dispersion. Winning is material evidence of over-estimation; Kagel-Levin experimental range (15-30%) applies."
applied: true
Contrast -- private-value case: Same rawvaluation = $30, but valuetype = private_value (target matters uniquely to this bidder). Haircut = 0. Adjusted = $30. Applied = false. Rationale: "No informational-asymmetry discount; winning is not adverse because other bidders do not value the target similarly."
Workflow
Copy this checklist and track progress:
Winner's-Curse Haircut Progress:
- [ ] Step 1: Classify value_type (common / private / mixed)
- [ ] Step 2: Validate inputs (range checks, N >= 1)
- [ ] Step 3: Short-circuit for private-value
- [ ] Step 4: Compute haircut_pct via formula
- [ ] Step 5: Apply haircut to raw_valuation
- [ ] Step 6: Emit structured output with rationale
Step 1: Classify value_type
Classification is a judgment call and MUST be explicit. The caller should pass it; this skill validates the choice against the decision tree in [resources/template.md](resources/template.md#value-type-decision-tree).
Step 2: Validate inputs
Step 3: Short-circuit for private-value
If valuetype == "privatevalue", skip the formula entirely:
haircut_pct = 0
adjustedvaluation = rawvaluation
applied = false
See [resources/methodology.md](resources/methodology.md#why-private-value-gets-zero-haircut) for the Bayesian reason this short-circuit is correct.
Step 4: Compute haircut percentage
For common_value targets:
haircut_pct = min(35, 10 + log(N) x 5 + signal_dispersion x 0.2)
The min(35, ...) ceiling hard-caps the haircut at 35% even at extreme N and dispersion. This reflects that empirical Kagel-Levin estimates rarely exceed 30%; 35% is the outer envelope.
For mixed targets, interpolate:
mix_common_weight = 0.6 (default; caller may override)
mix_private_weight = 1 - mix_common_weight
haircut_pct = mix_common_weight x (common_value_haircut) + mix_private_weight x 0
See [resources/methodology.md](resources/methodology.md#haircut-formula-derivation) for formula intuition (log-N captures the adverse-selection severity growing with more competitors; linear dispersion term captures the variance of bidder estimates).
Step 5: Apply haircut
adjusted_valuation = raw_valuation x (1 - haircut_pct / 100)
Step 6: Emit structured output
Return:
{
"adjusted_valuation": <number>,
"haircut_pct": <number in [0, 35]>,
"classification_rationale": "<one-sentence justification>",
"applied": <bool>
}
Validate using [resources/evaluators/rubricauctionwinnerscursehaircut.json](resources/evaluators/rubricauctionwinnerscursehaircut.json). Minimum standard: average score >= 3.5.
Common Patterns
Pattern 1: Headline Common-Value Target, Many Bidders
- Example: Top-100 prospect call-up in fantasy FAAB (N=6-8); prediction-market contract on a high-salience event; M&A target covered by many investment banks
- Typical inputs: N in [5, 10], signal_dispersion in [30, 60]
- Typical haircut: 22-32%
- Why: Strong adverse-selection; winning almost surely means you were highest of many similar estimates
- Watch for: Do not double-count with first-price shading -- the two are independent corrections (see
auction-first-price-shading for shading)
Pattern 2: Private-Value Complement (Handcuff)
- Example: Backup reliever to your own closer; land adjacent to land you already own; puzzle piece that fits only your collection
- Typical inputs: N = 1-2, valuetype =
privatevalue
- Typical haircut: 0% (short-circuited)
- Why: No adverse selection -- winning is not evidence you over-estimated, because others genuinely value the target less
- Watch for: Make sure the private-value claim is real. If three other bidders also have a complementary use, it is closer to common-value
Pattern 3: Mixed Late-Season Streaming Claim
- Example: Hot hitter whose projection everyone agrees on, but fits a specific category need you have; ad-auction keyword with a common CPC baseline but a bidder-specific conversion uplift
- Typical inputs: N in [3, 5], signaldispersion in [20, 40], valuetype =
mixed with mixcommonweight around 0.5-0.7
- Typical haircut: 10-18%
- Why: Partial adverse selection, partial private value
- Watch for: Explicitly estimate and record the common/private split; do not default to mixed when a clear binary classification applies
Pattern 4: Thin-Field Common Value (Low N)
- Example: Niche common-value target in a small auction pool (N = 2)
- Typical inputs: N = 2, dispersion anywhere
- Typical haircut: 14-18%
- Why: Even with only 2 bidders, common-value winner's curse operates -- winning means you exceeded the other informed estimate. Still smaller than large-N cases because adverse-selection severity grows with log(N)
- Watch for: Do not set haircut to 0 just because N is low; private-value requires a separate claim about value heterogeneity
Guardrails
- Classification must be explicit. Never infer
value_type silently from other inputs. The caller passes it; the skill validates. A missing or ambiguous classification is an error, not a default.
- Private-value short-circuit is absolute. If the caller asserts private-value, haircut is zero even when N is large. This is correct: if others genuinely value the target less, then their bids do not carry adverse information about your own estimate.
- Never stack this haircut with another winner's-curse correction. Downstream systems that already apply Bayesian bid shading (e.g., auction-first-price-shading's N-bidder shade) are correcting a different phenomenon (strategic shading for expected surplus). Apply both; do not apply either twice.
- Cap at 35%. The empirical Kagel-Levin range is 15-30%. The 35% ceiling provides headroom for very large N plus high dispersion but prevents the formula from producing absurd discounts (e.g., 80%).
- N >= 1. N = 1 means the bidder is alone;
log(1) = 0 so the formula yields haircutpct = 10 + signaldispersion x 0.2. For a true monopsony (no competing informed bidder), the caller should pass private_value instead -- there is no adverse-selection mechanism without competitors.
- Signal dispersion is a proxy, not a measurement. In practice it is rarely directly observable. Estimate from: historical bid-spread in comparable auctions, disagreement among public projection systems, or degree of public information asymmetry. Document the basis.
- Mixed value requires an explicit weight. Do not silently default to 0.6. Callers should state the common/private split and its justification. If they cannot, classify as commonvalue (conservative) or privatevalue (aggressive), not mixed.
- Domain-neutral contract. This skill does not know about FAAB, fantasy baseball, or any specific auction environment. Callers translate their domain inputs into the generic four-field contract; the skill returns a generic output which the caller then interprets.
Quick Reference
Core formula:
if value_type == "private_value":
haircut_pct = 0
applied = false
elif value_type == "common_value":
haircut_pct = min(35, 10 + log(N) x 5 + signal_dispersion x 0.2)
applied = true
elif value_type == "mixed":
common_haircut = min(35, 10 + log(N) x 5 + signal_dispersion x 0.2)
haircut_pct = mix_common_weight x common_haircut # default 0.6
applied = true
adjusted_valuation = raw_valuation x (1 - haircut_pct / 100)
Haircut lookup (common_value, approximate):
| N |
dispersion=0 |
dispersion=25 |
dispersion=50 |
dispersion=100 |
| 1 |
10.0% |
15.0% |
20.0% |
30.0% |
| 2 |
13.5% |
18.5% |
23.5% |
33.5% |
| 4 |
16.9% |
21.9% |
26.9% |
35.0% (cap) |
| 6 |
19.0% |
24.0% |
29.0% |
35.0% (cap) |
| 8 |
20.4% |
25.4% |
30.4% |
35.0% (cap) |
| 12 |
22.4% |
27.4% |
32.4% |
35.0% (cap) |
Input contract:
| Field |
Type |
Range |
Required |
raw_valuation |
number |
>= 0 |
yes |
value_type |
string |
commonvalue / privatevalue / mixed |
yes |
ninformedbidders |
int |
>= 1 |
yes |
signal_dispersion |
number |
[0, 100] |
yes |
mixcommonweight |
number |
[0, 1] |
only if mixed (default 0.6) |
Output contract:
| Field |
Type |
Range |
adjusted_valuation |
number |
[0, raw_valuation] |
haircut_pct |
number |
[0, 40] |
classification_rationale |
string |
one sentence |
applied |
bool |
false iff private-value |
Key resources:
- [resources/template.md](resources/template.md): Classification decision tree (common vs private vs mixed), worked examples (closer = common; handcuff = private; late-season hot hitter = mixed), input/output templates
- [resources/methodology.md](resources/methodology.md): Kagel & Levin experimental evidence, formal Bayesian posterior derivation, log-N and dispersion intuition, value-type classification heuristics with fantasy-FAAB examples, why private-value gets zero haircut
- [resources/evaluators/rubricauctionwinnerscursehaircut.json](resources/evaluators/rubricauctionwinnerscursehaircut.json): 8-criterion quality rubric