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.
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 neutralThe 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 price | Expected units | Expected revenue | Expected contribution profit |
|---|---|---|---|
| ₹90 | 140 | ₹12,600 | (90 - 60) × 140 = ₹4,200 |
| ₹100, current | 115 | ₹11,500 | (100 - 60) × 115 = ₹4,600 |
| ₹110 | 95 | ₹10,450 | (110 - 60) × 95 = ₹4,750 |
| ₹120 | 76 | ₹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.