← Back to the full chapter
Lesson

6. Optimize a constrained grid, with an explicit abstain path

Model demand, separate the causal response, and optimise within supported limits.

6 min readApplied extensionUpdated
Read source ↗

Choose a small price grid approved by the relevant business owners. Apply constraints before ranking and again after selection:

  • Price floors/ceilings, positive unit contribution where required, and a margin floor.
  • Maximum change from the current price, minimum time between changes, rounding and price endings.
  • Inventory, replenishment, contractual and campaign restrictions.
  • Consistent customer-facing price/quote rules and any applicable organisational policies.
  • Valid cost, competitor and feature timestamps, and candidate prices within identified support.

Decide what happens when the current price itself is no longer allowed. A fallback must come from a currently approved policy. If there is none, return review_required with no executable price. The optimiser cannot invent an exception to restore availability.

The policy flow

Before comparing expected profits, pass every candidate through feasibility, local support, predictive risk and a selection-adjusted improvement rule. Each check keeps only passing prices. The identification gate uses the design and assumptions in Section 4, not a predictive score.

flowchart TB accTitle: Price recommendation policy accDescr: Every approved price must pass support, feasibility, predictive-risk and selection-adjusted improvement checks before selection; otherwise use a valid approved fallback or request review. checks["Readiness + identification<br/>Gate every candidate"] passing{"Any pass?"} best["Highest mean profit"] recheck{"Still valid?"} apply["Log, then apply once"] fallback["Valid fallback<br/>or review"] checks --> passing passing -->|Yes| best passing -->|No| fallback best --> recheck recheck -->|Yes| apply recheck -->|No| fallback classDef data fill:#EFF6FF,stroke:#3973AC,color:#17324D,stroke-width:1.5px classDef model fill:#EFF2FD,stroke:#596FC2,color:#17324D,stroke-width:1.5px classDef decision fill:#FFF5DF,stroke:#A8751A,color:#17324D,stroke-width:1.5px classDef execution fill:#E9F6F2,stroke:#23866E,color:#17324D,stroke-width:1.5px classDef neutral fill:#F5F7FA,stroke:#8796A8,color:#17324D,stroke-width:1.5px class best model class checks,passing,recheck decision class apply execution class fallback neutral
Every approved price must pass support, feasibility, predictive-risk and selection-adjusted improvement checks before selection; otherwise use a valid approved fallback or request review.
Diagram source
%%{init: {"theme":"base","themeVariables":{"fontFamily":"Arial, sans-serif","fontSize":"16px","textColor":"#17324D","primaryColor":"#F5F7FA","primaryTextColor":"#17324D","primaryBorderColor":"#8796A8","lineColor":"#718096","secondaryColor":"#EFF6FF","tertiaryColor":"#E9F6F2","clusterBkg":"#F8FAFC","clusterBorder":"#D6DEE8","edgeLabelBackground":"#FFFFFF"},"flowchart":{"htmlLabels":true,"nodeSpacing":18,"rankSpacing":28,"curve":"linear","padding":10,"subGraphTitleMargin":{"top":8,"bottom":12}}}}%%
flowchart TB
    accTitle: Price recommendation policy
    accDescr: Every approved price must pass support, feasibility, predictive-risk and selection-adjusted improvement checks before selection; otherwise use a valid approved fallback or request review.

    checks["Readiness + identification<br/>Gate every candidate"]
    passing{"Any pass?"}
    best["Highest mean profit"]
    recheck{"Still valid?"}
    apply["Log, then apply once"]
    fallback["Valid fallback<br/>or review"]

    checks --> passing
    passing -->|Yes| best
    passing -->|No| fallback
    best --> recheck
    recheck -->|Yes| apply
    recheck -->|No| fallback

    classDef data fill:#EFF6FF,stroke:#3973AC,color:#17324D,stroke-width:1.5px
    classDef model fill:#EFF2FD,stroke:#596FC2,color:#17324D,stroke-width:1.5px
    classDef decision fill:#FFF5DF,stroke:#A8751A,color:#17324D,stroke-width:1.5px
    classDef execution fill:#E9F6F2,stroke:#23866E,color:#17324D,stroke-width:1.5px
    classDef neutral fill:#F5F7FA,stroke:#8796A8,color:#17324D,stroke-width:1.5px
    class best model
    class checks,passing,recheck decision
    class apply execution
    class fallback neutral

The first stage checks input readiness and causal identification, then tests every approved price for feasibility, local support, predictive risk and selection-adjusted mean improvement. If a precondition fails, no candidate passes. Pick the highest expected profit among the survivors only, recheck the current state, and log durably before delivery. Revalidate the approved fallback before using it. If none is still valid, execute nothing and return review_required.

A worked example

Assume a cost of ₹60 per unit, enough inventory, no extra fees or returns, and the following illustrative identified mean demand for one day. These made-up values show the arithmetic, not an elasticity estimate from a public dataset.

Approved priceExpected unitsExpected revenueExpected contribution profit
₹90140₹12,600(90 - 60) × 140 = ₹4,200
₹100, current115₹11,500(100 - 60) × 115 = ₹4,600
₹11095₹10,450(110 - 60) × 95 = ₹4,750
₹12076₹9,120(120 - 60) × 76 = ₹4,560

Under these assumptions, ₹110 has the highest expected contribution profit: ₹150 above the current policy, about 3.26%. ₹90 maximises the table's revenue, a different objective. A 5% change limit from ₹100 would rule ₹110 out. If uncertainty about the profit difference still includes an unacceptable loss, a conservative rule would keep the approved fallback, even though ₹110 has the highest estimated mean.

Try it yourself. A supplier cuts the unit cost to ₹50 and the demand estimates are unchanged. Which approved price now has the highest expected contribution profit, and what should the policy recommend?

Hint and explained answer

Recompute (price - 50) × units. ₹90 gives 40 × 140 = ₹5,600, ₹100 gives 50 × 115 = ₹5,750, ₹110 gives 60 × 95 = ₹5,700 and ₹120 gives 70 × 76 = ₹5,320. The current ₹100 now wins, ₹50 ahead of ₹110, so the policy keeps the current price. A lower cost makes each extra unit worth more and moves the best price lower. Revenue still favours ₹90, because revenue ignores cost. The ₹50 margin, under 1% of profit, shows how a cost change can flip a close choice. Check such gaps with paired uncertainty before changing a price.

Use paired estimates of profit difference for a conservative improvement bound. Subtracting the ends of two unrelated prediction intervals does not give a confidence interval for lift. Choose minimum improvement and loss thresholds before seeing experimental results. To pair estimates, refit on the same bootstrap sample, or take the same parameter-posterior draw, for every price. This captures covariance in the estimated mean responses. Randomised price data can identify each price's marginal outcome distribution under the design assumptions, not one person's joint potential outcomes across prices.

To estimate model uncertainty, refit the demand and nuisance estimators with a dependence-aware resampling scheme. Resampling fixed predictions alone misses that fitting uncertainty. Label a posterior bound as conditional on its model and prior, not as a distribution-free confidence guarantee. If live monitoring tests for improvement repeatedly, fix an analysis schedule in advance or use justified sequential inference. A fixed-sample interval does not allow unlimited peeking.

# Pseudocode: response inference and predictive risk are separate dependencies.
def recommend(context, policy, response_model):
    fallback = policy.valid_fallback(context)  # Can be None: then review_required.
    if not context.features_fresh or not context.costs_valid:
        return abstain(fallback, reason="stale_or_invalid_inputs")
    if not response_model.price_effect_identified:
        return abstain(fallback, reason="unvalidated_price_effect")

    candidates = [
        p for p in policy.approved_prices(context)
        if policy.allows(p, context)
        and response_model.has_local_support(p, context)
    ]
    if not candidates or fallback is None:
        return abstain(fallback, reason="no_supported_feasible_action")
    if not response_model.has_local_support(fallback, context):
        return abstain(fallback, reason="cannot_compare_with_fallback")

    # Each refitted bootstrap model (or parameter draw) estimates each price's
    # marginal demand distribution. Integrate that distribution to calculate
    # expected profit, applying inventory and costs once. No paired customer
    # outcomes across counterfactual prices are assumed.
    fits = response_model.inference_fits(context)
    mean_profit = policy.integrated_profit_by_fit(
        fits, prices=candidates + [fallback], context=context)
    passing_prices = {}
    for price in candidates:
        # Predictive variation governs operating risk, such as stockouts or
        # a future-period loss limit. Check every candidate before selection.
        demand = response_model.predictive_demand_distribution(price, context)
        future_profit = policy.predictive_profit_distribution(demand, price, context)
        if not policy.operating_risk_acceptable(future_profit):
            continue
        mean_gain = mean_profit[price] - mean_profit[fallback]
        if not policy.mean_improvement_supported(
                mean_gain, candidate_family=mean_profit):  # Selection-adjusted rule.
            continue
        passing_prices[price] = future_profit
    if not passing_prices:
        return abstain(fallback, reason="no_candidate_passes_evidence_and_risk")
    best = max(passing_prices, key=lambda p: mean_profit[p].mean())
    if not policy.allows(best, context):
        return abstain(fallback, reason="constraints_changed")
    return decision(best, alternatives=candidates, mean_profit_summary=mean_profit,
                    predictive_risk_summary=passing_prices[best],
                    evidence=response_model.evidence_version)

The support check and improvement rule are contracts you implement, not magic confidence scores. Define their diagnostics, estimation assumptions and thresholds in the policy card, and record both gates there. A full future-profit distribution supports risk decisions; statistical confidence in mean lift is a different calculation. Validate the marginal predictive distributions used for risk, and let the mean-improvement inference account for picking the best of several candidates. operating_risk_acceptable must reject an unvalidated distribution or inventory regime, so a mean forecast cannot quietly pass as a predictive-risk certificate. Include estimation uncertainty and plausible cost/return scenarios in the risk sensitivity checks. Apply the adjustment to the full supported, feasible candidate family, and do not narrow it after seeing risk or profit estimates. State may change after scoring, so revalidate cost, inventory and policy version when applying a recommendation.

Something unclear or out of date? Suggest a correction ↗

Find your next idea

Loading local search…

Search is built from this edition. No account needed.