A studio sets every internal price in its own economy, which makes demand measurable without ever randomizing a dollar price. Three experiments identify one parameter each: the own-price elasticity of a repeatable sink, the income elasticity of the same sink under a wealth shock, and the cost elasticity of a durable good.
Two rules shape everything below. Units, not currency — elasticity is a response in quantity, and measuring it on currency spent bakes the price into the outcome. And the exposure log is the denominator — a player who never saw the price cannot have declined it, so the analysis is built on price impressions rather than on purchases.
## SRM: control=2402, price_down_10=2377, price_down_25=2344, price_up_15=2316 chi-square p = 0.615 -> PASS
## price exposures: 22,636 player-days across 4,902 players
| variant_name | price | exposures | units_per_day | buy_rate |
|---|---|---|---|---|
| control | 180 | 5699 | 2.5050 | 0.8993 |
| price_down_10 | 162 | 5789 | 2.8055 | 0.9278 |
| price_down_25 | 135 | 5729 | 3.4835 | 0.9576 |
| price_up_15 | 207 | 5419 | 2.1495 | 0.8749 |
Four price points give a curve rather than a single arc. The
estimator is a Poisson GLM with a log link — unit
counts are non-negative integers with a mass at zero, so the Poisson
coefficient on log(price) is the elasticity.
Standard errors are clustered on the player, since each contributes many
days.
## own-price elasticity: -1.13 95% CI [-1.20, -1.06]
## demand is elastic -> a price rise lowers gold spent on this sink
| country_tier | exposures | elasticity | ci_low | ci_high |
|---|---|---|---|---|
| US | 2665 | -0.923 | -1.124 | -0.723 |
| Tier 1 | 12707 | -1.065 | -1.160 | -0.970 |
| APAC | 4256 | -1.229 | -1.392 | -1.065 |
| ROW | 3008 | -1.453 | -1.645 | -1.261 |
A player cannot buy more than the balance affords. Whether that binds here is an empirical question worth asking before trusting the pooled number. Wallet depth is measured pre-window — a price cut itself raises balances, so a within-window balance would be post-treatment and the strata would not be comparable across arms.
## days where the purchase hit the affordability ceiling: 7.6%
## The elasticity is flat across wallet depth and the intervals overlap heavily, so
## for THIS test the budget constraint is not distorting the pooled estimate.
## The same check on the income shock in section 5 comes out the other way.
| stratum | exposures | bunched_at_constraint | elasticity | ci_low | ci_high |
|---|---|---|---|---|---|
| deep wallet (12+ units) | 11155 | 0.007 | -1.162 | -1.273 | -1.051 |
| mid wallet (6-11) | 8522 | 0.084 | -1.097 | -1.202 | -0.991 |
| thin wallet (<=5) | 2959 | 0.308 | -1.133 | -1.304 | -0.962 |
Test 9013 multiplies each player’s gold income by 1.0, 1.35 or 1.9
while leaving the price alone. Because the shock is
multiplicative, log wealth shifts by exactly
log(multiplier) for everyone in the arm, so the arm
contrast identifies the income elasticity with no bias from averaging
heterogeneous baseline incomes.
## income elasticity, all players : +0.68 95% CI [+0.64, +0.72]
## income elasticity, deep wallets: +0.63 95% CI [+0.56, +0.70]
##
## The two differ because a wealth shock does two things: it shifts preferences (the
## structural parameter) and it relaxes the budget constraint. Only players whose
## wallet never binds isolate the first.
| income_multiplier | mean | sem | n |
|---|---|---|---|
| 1.00 | 2.4678 | 0.0203 | 5925 |
| 1.35 | 3.0553 | 0.0241 | 5751 |
| 1.90 | 3.8195 | 0.0276 | 5873 |
A base upgrade is built once and never consumed, so “how many” is the wrong question. Each eligible player-day is a chance to start the next upgrade, and the estimand is a hazard. The risk set is days on which at least one recipe was affordable, and recipe identity is absorbed so the only remaining variation in cost is the randomized multiplier.
## risk days: 15,988 upgrades started: 1,758
## cost elasticity of the start hazard (log-odds): -0.62 95% CI [-1.04, -0.19]
| variant_name | risk_days | start_rate | mean_cost |
|---|---|---|---|
| both_down | 3364 | 0.1204 | 352.3947 |
| control | 3469 | 0.0974 | 469.1663 |
| metal_down | 3062 | 0.1153 | 406.0002 |
| mix_neutral | 2985 | 0.1116 | 456.4978 |
| oil_down | 3108 | 0.1059 | 406.2341 |
Because a recipe needs both resources in fixed proportions there is no substitution within it — but there is substitution across recipe families, and that is where a cost change shows up. The intuition to resist: discount the resource a player is short of. Players have already routed around scarcity, so their spending is concentrated in the other resource, which is where a discount actually lands.
| variant_name | upgrades | metal_heavy_share |
|---|---|---|
| both_down | 405 | 0.6049 |
| control | 338 | 0.5533 |
| metal_down | 353 | 0.7054 |
| mix_neutral | 333 | 0.6066 |
| oil_down | 329 | 0.4954 |
The true parameters are recorded because this is a simulated case study. The analysis above never reads them; this coda does, to say whether the method recovers what generated the data.
| parameter | estimate | truth | abs_error |
|---|---|---|---|
| own-price elasticity (pooled) | -1.132 | -1.090 | 0.043 |
| elasticity | US | -0.923 | -0.770 | 0.153 |
| elasticity | Tier 1 | -1.065 | -0.990 | 0.075 |
| elasticity | APAC | -1.229 | -1.265 | 0.036 |
| elasticity | ROW | -1.453 | -1.540 | 0.087 |
| income elasticity (deep wallets) | 0.627 | 0.594 | 0.033 |
| durable cost elasticity | -0.617 | -0.850 | 0.233 |
Three parameters, recovered from an economy where no dollar price was ever randomized: demand for the repeatable sink is elastic, income raises it by roughly six tenths of a percent per percent of extra gold, and durable upgrades respond to their recipe cost through timing rather than quantity.
What to carry forward:
Generated by the Savepoint Analytics video-game A/B testing case
study. All data is simulated; the demand engine is
data/simulation/economy.py.