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.

1. The panel: one row per price exposure

## 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
Four price points, one sink
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

2. The demand curve

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

3. Elasticity is not one number — it varies by market

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

4. Is the budget constraint distorting this?

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

5. Income: a wealth shock at an unchanged price

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

6. Durable upgrades: a hazard, not a count

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]
Cheaper recipes get built sooner
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

7. Checking the answers

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.

Estimated versus true — the analysis never reads this table
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

Conclusion

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:

  1. Count the units, and count the exposures. The exposure log is what makes the denominator honest; without it, “did not buy” and “never saw the price” are the same row.
  2. Report elasticity by market, not just pooled. The pooled number averages answers that differ by more than 50% across tiers, and the pricing decision is made per market.
  3. Check whether the budget constraint binds — do not assume. It did not distort the price test here, but a wealth shock relieves the constraint directly, so the pooled income elasticity overstates the structural one.
  4. Durable goods are a timing problem. Model the hazard over the eligible risk set, absorb the item identity, and expect the effect to fade as everyone eventually builds.

Generated by the Savepoint Analytics video-game A/B testing case study. All data is simulated; the demand engine is data/simulation/economy.py.