← Back to Blog
Quantitative MarketingRisk ManagementPromo EconomicsFinancial Engineering

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.

SPSantosh Paudel· July 31, 2026· 9 min read· 1 views
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

CapAmountHealthy campaigns killed
1.00x expected$70,18246.37%
1.10x expected$77,20133.42%
1.25x expected$87,72818.51%
1.50x expected$105,2745.46%
1.516x expected$106,4065.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

LayerSet atPurposeAction
Per-user capPayout distribution P99 per userStops individual abuseBlock further redemptions for that user
Cumulative campaign cap1.52x expected totalStops a runaway campaignPause, notify, require approval to resume
Velocity alertCumulative tracking above P90 for the dayEarly warning, not a stopNotify only, no automatic action
Anomaly flagPayout distribution shape shiftsDetects a broken offer or exploitInvestigate, 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.

Free resource

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.

No spam. Unsubscribe anytime.

Browse all free guides →

Want to implement this with guidance?

Santosh helps founders turn insights like this into real systems.

Promo Economics Audit

External Resources

Further Reading & Tools

Related Posts

01
8 min
Incentive DesignBehavioral Economics
TodayQuantitative Marketing

The Line Between Motivating and Manipulating: An Incentive Design Ethics Test

Every mechanic in this series works because of a cognitive bias. That makes them powerful and makes some of them indefensible. A six-question test for which side of the line your design sits on, and what regulators have already acted against.

Read article
02
8 min
Price DiscriminationPricing
TodayQuantitative Marketing

Why Promo Codes Beat Sales: Price Discrimination in Practice

A blanket 20% discount earns $20,000 less than no discount at all. The same discount behind a code that only bargain hunters bother to find earns $64,000 more. The friction is the entire mechanism.

Read article
03
8 min
FloatBreakage
TodayQuantitative Marketing

Float, Breakage and the Stored-Value Balance Sheet

Starbucks held $2.12 billion in customer-loaded accounts as of Q1 FY2026 and recognised $207.6 million of breakage revenue in 2024. Prepaid balances are interest-free working capital. Here is how to model your own.

Read article