Value at Risk and Kill Switches for Live Promotions
A spend cap set at expected payout shuts down 46% of perfectly healthy campaigns. The right multiple is 1.52x. Sizing exposure limits and automated kill switches against a real payout distribution.
Table of contents
Once a promo is live, budgeting stops and risk management starts. The question is no longer what it will cost but at what point you intervene, and that threshold has to be sized against the distribution rather than the plan.
Set it wrong in one direction and you kill healthy campaigns. Set it wrong in the other and the switch never fires when it matters.
The short answer
Simulating 100,000 thirty-day campaigns, a cumulative spend cap set at the expected payout trips on 46.37% of perfectly healthy campaigns. A cap at 1.25x expected trips 18.51%. To hold false triggers to 5% you need 1.516x expected, which is $106,406 against an expected $70,182. A daily cap set at the daily 99th percentile trips 17.9% of healthy campaigns, which is why a daily limit alone is the wrong instrument.
Value at risk, translated
VaR answers: over a given period, at a given confidence level, what is the most this loses? A one-day 95% VaR of $4,452 means that on 95 days out of 100 the payout stays below $4,452.
Applied to marketing it gives you three things a budget does not. A defensible reserve. A per-campaign exposure limit. And a trigger level for automated intervention with a stated false-positive rate, which is the part that makes it operationally usable.
The simulation
Take the payout model from before, spread it across 30 days, and run 100,000 campaigns.
import math
import numpy as np
def campaign_risk(eligible=50_000, days=30, alpha=12.0, beta=188.0,
payout_median=22.0, payout_sigma=0.35,
campaigns=100_000, seed=42):
rng = np.random.default_rng(seed)
daily_elig = eligible // days
# One rate per campaign, held across its days: campaigns differ from each
# other more than days differ within a campaign.
rate = rng.beta(alpha, beta, (campaigns, 1))
redeemed = rng.binomial(daily_elig, np.broadcast_to(rate, (campaigns, days)))
daily = redeemed * rng.lognormal(math.log(payout_median), payout_sigma,
(campaigns, days))
cum = daily.cumsum(axis=1)
final = cum[:, -1]
return daily, cum, final
daily, cum, final = campaign_risk()
print(f"daily payout mean ${daily.mean():>9,.0f} "
f"VaR95 ${np.percentile(daily, 95):>9,.0f} "
f"VaR99 ${np.percentile(daily, 99):>9,.0f}")
print(f"campaign total mean ${final.mean():>9,.0f} "
f"P95 ${np.percentile(final, 95):>9,.0f}")
print("\ncap sizing (false-trigger rate on healthy campaigns):")
for mult in (1.00, 1.10, 1.25, 1.50):
cap = final.mean() * mult
trips = (cum >= cap).any(axis=1).mean()
print(f" {mult:.2f}x expected (${cap:>9,.0f}) trips {trips:>6.2%}")
Output:
daily payout mean $ 2,339 VaR95 $ 4,452 VaR99 $ 5,996
campaign total mean $ 70,182 P95 $ 106,406
cap sizing (false-trigger rate on healthy campaigns):
1.00x expected ($ 70,182) trips 46.37%
1.10x expected ($ 77,201) trips 33.42%
1.25x expected ($ 87,728) trips 18.51%
1.50x expected ($ 105,274) trips 5.46%
The campaign P95 here is $106,406, noticeably tighter than the $131,821 from the single-shot budget model. That is not a discrepancy. Spreading the campaign over 30 days means 30 independent draws of the average payout instead of one, and averaging 30 draws shrinks the variance of that term considerably. The redemption rate still varies campaign to campaign, which is why meaningful spread remains. Which model to use depends on the question: use the wider one to size a reserve for a single short burst, and this one to set thresholds on a campaign that runs daily.
The false-trigger problem
| Cap | Amount | Healthy campaigns killed |
|---|---|---|
| 1.00x expected | $70,182 | 46.37% |
| 1.10x expected | $77,201 | 33.42% |
| 1.25x expected | $87,728 | 18.51% |
| 1.50x expected | $105,274 | 5.46% |
| 1.516x expected | $106,406 | 5.00% |
A cap at the expected payout, which sounds like the obvious choice and is what most teams set, halts nearly half of campaigns that were behaving exactly as designed. The reason is the same skew from the budgeting model: the median campaign is below the mean, but a large minority run above it for entirely ordinary reasons.
Every false trigger has a real cost. Someone investigates, the campaign is paused mid-flight, the offer is pulled from users who were promised it, and the team learns to ignore the alert. That last one is the expensive failure, because it converts a risk control into noise.
Why a daily cap alone does not work
The instinct is to cap daily spend, since it is easier to reason about. Set the daily limit at the daily 99th percentile of $5,996, which sounds extremely permissive.
Across a 30-day campaign, that cap trips on 17.9% of perfectly healthy campaigns.
The arithmetic is the multiple comparisons problem. A threshold that a single day exceeds 1% of the time gets 30 chances to be exceeded. The probability of at least one breach is 1 - 0.99^30 = 26% in the independent case, moderated here to 17.9% because days within a campaign are correlated through the shared redemption rate.
Any per-period threshold has this property, and it gets worse the more often you check. Cap on the cumulative total, or on a rolling window, and set the threshold from the simulated distribution of that specific statistic rather than from the daily one.
A layered structure that works
| Layer | Set at | Purpose | Action |
|---|---|---|---|
| Per-user cap | Payout distribution P99 per user | Stops individual abuse | Block further redemptions for that user |
| Cumulative campaign cap | 1.52x expected total | Stops a runaway campaign | Pause, notify, require approval to resume |
| Velocity alert | Cumulative tracking above P90 for the day | Early warning, not a stop | Notify only, no automatic action |
| Anomaly flag | Payout distribution shape shifts | Detects a broken offer or exploit | Investigate, do not auto-pause |
Separating alerts from stops is the part most implementations miss. A velocity alert that notifies costs almost nothing when wrong and buys you a day of lead time. A hard stop should be rare, defensible, and sized to a false-trigger rate you have written down.
Setting the per-user cap
Individual abuse looks different from aggregate overspend, and a campaign-level cap will not catch it in time.
def per_user_cap(payouts, confidence=0.99):
"""Cap individual redemption value at a percentile of observed payouts."""
return float(np.percentile(payouts, confidence * 100))
rng = np.random.default_rng(42)
sample = rng.lognormal(math.log(22.0), 0.35, 100_000)
for c in (0.95, 0.99, 0.999):
print(f" P{c*100:>5.1f} cap ${per_user_cap(sample, c):>8,.2f}")
Output:
P 95.0 cap $ 39.15
P 99.0 cap $ 49.96
P 99.9 cap $ 65.04
A cap at the 99.9th percentile affects one legitimate user in a thousand and removes the entire right tail available to someone gaming the offer.
What this looks like in practice
Before launch, simulate the payout distribution and write down three numbers: the reserve at P95, the cumulative cap at the multiple that gives your accepted false-trigger rate, and the per-user cap. Put them in the campaign brief.
Wire the cumulative cap into the system that issues the offer, not into a dashboard someone checks. A control that depends on a person noticing is not a control.
Record every trigger, including false ones, and recalibrate quarterly. If the switch never fires, it is set too loose to be doing anything. If it fires often, it is generating the noise that will eventually get it ignored.
The whole point of pricing an offer before launch is to still have choices. Once it is live, the only remaining choice is when to stop, and that is worth deciding with a number rather than a nerve.
Get the Promo & Bonus Economics Workbook
Every formula for pricing a promo before you launch it: expected value, wagering cost, breakage, breakeven conversion. Worked examples with real executed numbers.
Browse all free guides →Want to implement this with guidance?
Santosh helps founders turn insights like this into real systems.
External Resources
Further Reading & Tools
Investopedia — Financial Engineering
Reference on the field, its three pillars, and its primary applications
CFA Institute — Refresher Readings
Curriculum material on derivatives valuation, probability, and risk measurement
QuantLib
Open-source quantitative finance library, reference for how pricing models are structured
arXiv q-fin
Preprints in quantitative finance covering pricing, risk management, and market microstructure