Back to portfolio
Case StudyAlgorithmic Optimization

Bonus Payout Curve Generator

A parametric payout-curve generator tuned by a genetic algorithm to hit a target budget utilization while keeping engagement inside a stakeholder-defined band.

The Challenge

Designing a fair bonus curve is a six-parameter, non-convex problem - start/mid/max points and bonuses all interact, and the curve must respect budget while sustaining engagement.

The Solution

Piecewise fit: exponential growth from start→mid, saturation growth from mid→max. Wrap it in a DEAP GA that drives budget utilization toward 100% (minimizing |100 − utilization|) with penalties for engagement out of band.

Impact

Utilization climbed from 85.5% → 93.4% at a clean 90.0% engagement rate. Manual judgment replaced by a reproducible, auditable calibration.

PythonNumPySciPyDEAP (GA)PandasPlotly
Baseline

Before optimization

Before optimization
People
999
960 ≥ start
Engagement
96.1%
Meaningful 53.75%
Budget Util.
85.5%
$85,410.36 / $99,900
Total Sales
$998k
Percentile · Bonus % · Performance %
PBonus %Perf %
1019.9193.0
2031.5595.0
3050.0397.0
4079.3899.0
50100.00100.0
60108.53101.8
70113.34103.0
80119.90105.0
90127.19108.0
100139.50119.0
Sensitivity Analysis (±10% sales)
+10% Sales
124.02%
cost $123,897.01 · sales $1098k
-10% Sales
18.03%
cost $18,007.68 · sales $899k

GA-tuned parameters

100 individuals · 50 generations · tournament select (k=3) · two-point crossover · uniform-int mutation (indpb=0.2) · DeltaPenalty for monotonicity infeasibility.

Start Pt
87
Mid Pt
98
Max Pt
110
Start Bonus
15
Mid Bonus
101
Max Bonus
150
Optimized

After optimization

After optimization
People
1,000
900 ≥ start
Engagement
90%
Meaningful 48.1%
Budget Util.
93.38%
$93,382.5 / $100,000
Total Sales
$1000k
Percentile · Bonus % · Performance %
PBonus %Perf %
1015.0087.0
2022.7689.2
3037.2592.0
4072.8896.0
50108.8099.0
60131.16103.0
70146.32108.0
80150.00112.0
90150.00119.0
100150.00125.0
Sensitivity Analysis (±10% sales)
+10% Sales
127.1%
cost $127,096.27 · sales $1100k
-10% Sales
51.03%
cost $51,033.81 · sales $900k

Methodology

1. Piecewise curve fit

Performance below start_point → 0 bonus. Above max_point → capped at max bonus. Between, two regimes:

lower_section · exponential fit
def fit_exponential(self, x, y):
    log_y = np.log(y)
    m, c = np.polyfit(x, log_y, 1)
    exponent = np.exp(m)
    return exponent**x * np.exp(c)
upper_section · saturation growth
def saturation_growth_model(self, x, L, k):
    return L - (L - self.mid_bonus) * np.exp(
        -k * (x - self.mid_point)
    )

# fit with bounds and p0 around mid/max bonus
popt, _ = curve_fit(model, x, y,
    p0=(max_bonus, 1e-3),
    bounds=([mid_bonus, 0], [max_bonus, 10]))

2. Objective & feasibility

Objective = |100 − budget_utilization| — the absolute gap to a 100% spend target, so over-spend and under-spend are both penalized. Engagement-rate violations add a fixed penalty. Monotonic ordering of points/bonuses enforced via DEAP's DeltaPenalty.

objective + GA wiring
def objective(x):
    sp, mp, xp, sb, mb, xb = np.array(x).round().astype(int)
    payout = PayoutGen(data, sp, mp, xp, sb, mb, xb, avg_bonus_amt).gen_payout()

    obj = abs(100 - payout.stats.budget_utilization)
    if not 80 <= payout.stats.engagement_rate <= 90:
        obj += 1000
    if payout.stats.meaningful_engagement_rate < 50:
        obj += 1000
    return obj

toolbox.register("evaluate", lambda ind: (objective(ind),))
toolbox.decorate("evaluate", tools.DeltaPenalty(feasible, 1000.0, distance))
toolbox.register("mate",   tools.cxTwoPoint)
toolbox.register("mutate", tools.mutUniformInt, low=lows, up=highs, indpb=0.2)
toolbox.register("select", tools.selTournament, tournsize=3)

algorithms.eaSimple(pop, toolbox, cxpb=0.7, mutpb=0.2, ngen=50)

3. Sensitivity analysis

For every candidate curve, sales are perturbed ±10% and attainment is re-bucketed against the fitted payout. The baseline curve was brittle (18% utilization at −10% sales); the optimized curve is more robust (51% utilization at −10% sales) at the cost of slightly higher upside exposure (127% vs 124%).