How the pricing engine decides
The pricing engine's job is gross profit and velocity: to find the price moves that earn more margin or sell more units, and to prove it. It is not a competitor-matching tool. Competitor prices are one input; a KVI is held at market on purpose, and everything else is priced on your own sales.
The operator stays the author
You write the strategy on Setup: the margin floor, rounding, the objective, the parity target, the no-touch list. The engine works inside those limits and never changes them. It proposes; a person decides. The KVI list, every product's role, every price event and every push are yours.
Each product has a role
Before it prices a line, the engine asks what the product is for. A traffic driver starts baskets: it aims at units, answers to the KVI and traffic-driver floor, and is never raised. A key value item is a mark beside the role, not a role: it is priced to the shelf next door, aims at units unless its role names another aim, answers to the same floor, and is raised only as far as a market two shops make; a KVI that is also a traffic driver is never raised. A basket builder aims at revenue without losing gross profit. A margin earner - every product nobody has given another role - aims at the objective you set for its family. Clearance aims at cash back, may go down to cost and is never raised. A gift with purchase is not priced at all.
The role comes before the family: a traffic driver aims at units even in a family whose objective is gross profit. BudLogix proposes a role for every stocked product each Sunday from 90 days of baskets; the engine acts only on the role a person chose. See Pricing roles.
Propose, hold, measure
The engine has three moves.
-
Propose. Generators price lines inside a draft: Expiry markdown, KVI to market, Merit gap, Parity headroom, Headroom. Each draft shows its cost and, where evidence exists, its projected return.
An expiry markdown is proposed only where it rescues stock. The engine works out how much of the lot would expire unsold at today's sell rate, and how much at the marked-down rate, and credits the markdown with the difference at cost, not with the whole lot. A lot that sells out before its date anyway, or a product that is not selling at all, gets no markdown: a price move cannot help either.
-
Hold. Where the evidence does not support a direction, the engine declines. The same stance sentence appears on the hub, Merit gaps and Results: "Raises are held: no raise has enough evidence." A hold is the engine refusing to guess, not a fault.
-
Measure. Every pushed event is measured for 28 days - four whole weeks, so both windows hold the same weekday mix - and the measured moves feed the price response bins that decide the next hold or allow.
What "held" means
Held means the measured response does not support a move in that direction. Raises are held until enough measured raises exist and they earned gross profit; cuts the same. Before any event has been measured the engine holds every price, which is why the first weeks after accepting a band run look quiet. The way out is to run a controlled event - the hub's Worth learning section names the cheapest ones and what each would teach - and let its window close. The hub recommends only moves measured to gain gross profit; a move measured to lose is held back and counted, never offered as something to do.
A range that spans zero is not a recommendation
Every projection carries a range as well as a number, 80% likely (see How results are measured). Where that range runs from a loss to a gain, the measured moves cannot say which will happen, and the engine files the move under Worth learning instead of What to do - whatever the middle number is. The range is not decoration; it decides which list a move appears in.
When two reasons disagree
Several reasons can want to price the same product. Merit says it sits above its band; the KVI list says the market has moved; there is unspent room in its bucket. Every one of them is asked about every product, and the engine picks between them rather than taking whichever was configured first.
The order it decides in:
- An expiry markdown wins outright. A lot with a date on it is not a shelf-position question, and the stock will not be there to sell later.
- A reason whose range spans no change loses to one whose range does not - even when its middle number is higher. A projection that cannot say whether a move gains or loses is not a better bet than a smaller one the evidence can place.
- Otherwise the higher expected gross profit wins, and on a tie the smaller move.
Two reasons naming the same price are not a disagreement. They become one proposal carrying both names, because two reasons agreeing is a stronger statement than either alone.
Whichever wins, the others are kept. The line's reason chip on the event reads "Headroom · chosen over KVI to market", and hovering it lists each rejected candidate with its price, what it was projected to earn, and whether its range spanned no change. A price chosen over another is a decision, and a decision nobody can see the alternative to is one you have to take on trust.
The forecast counts the whole shelf
Where the demand forecast speaks - a draft's Expected column and Returns, and Let the engine choose - its figure is what the move earns net of what it does to the rest of its bucket. A cut that sells more mostly by pulling sales off the eighths beside it has not earned what its own till says, and is not proposed as a gain. A raise that loses sales mostly to the strains beside it keeps the margin they pick up.
Moves on products mostly given away are left out of what the forecast learns: their paid units are what is left after the giveaways, and they distort the rest (see Pricing roles).
The forecast learns how much moves from every price move the store has measured: what the rest of the bucket sold over the same two windows, beside what the moved products gained or lost. That share is priced at what the rest of the bucket earns a unit today. On a draft, a line that takes from or hands to its neighbours says so under its figure ("after $40 taken from its neighbours"). Lines moved in one bucket are read together, so two cuts on one shelf cannot each take the same neighbours' sales.
Where the share can't be told for a kind of move and there is a shelf beside it, the line reads "Can't say yet" rather than counting nothing. The adjustment runs only while it predicts what whole buckets earned better than the move's own sales alone; the Sunday refit checks that each week, and switches it off by itself if it stops. See How results are measured.
A discount's length counts
A discount that runs a week has sold more a day here than the same discount run for a month. Where the forecast speaks for discounts, it reads how long the promotion runs, as well as how deep it is. At one BudLogix store (October 2026) a promotion half as long sold about 23% more a day, with a range of 14% to 33%. A discount test's draft is asked at the test's own length, and Plan a promotion at the run it recommends.
The forecast keeps this only while it predicts promotions it has not seen better than without it; the Sunday refit checks that each week. Shelf price moves have no length: a new shelf price stays until it is changed.
The expectation is frozen when you push
The Return figure on a draft moves with its inputs: the measured response is re-derived every week and the 90 days of trading behind it roll forward every day. So the moment you push, the engine writes down what it expected - the figure, its range, and which evidence it came from - and never touches it again.
That is what makes the closed event honest. It shows what was promised beside what was earned, and the promise is the one you read before you pressed the button, not a tidier one worked out afterwards. Where nothing could be projected at all, the push records that too: "no expectation" is a fact about the push, not a gap.
A cut is tested as a discount first
The engine will not propose a shelf cut it cannot judge. Where the evidence for a cut of that size is thin, it offers a discount test instead: the same products, the same depth, for a fortnight, as a discount somebody creates in Dutchie. The discount ends on its own date; a shelf cut has to be taken back, and customers remember it. You can also start one yourself, for any brands and format, from Start an event on Pricing > Events.
The answer lands in the engine's evidence about discounts of that size rather than about shelf cuts of that size, because that is what was measured. When the discount has earned a gross-profit gain against the products held back, the shelf move becomes a recommendation and the event offers to make it the price. See Start a discount test.
Raises are never tested this way. There is no temporary raise a customer would not simply wait out.
Raises come in two halves
A cut can be tested as a discount that ends on its own date. A raise cannot - there is no temporary raise a customer would simply not wait out - so the engine tests one the only other way it can: on half the family, with the other half standing still.
Where a draft is all raises and has enough lines to halve, it is drafted as the first half. Half the products move, the other half are held at their old price as the control, and the event is named "— raise, first half". The hub row says "first half" too, so what is being offered is clear before it is opened.
When the first half's window closes, the engine compares the two halves. If the raised half earned as much gross profit as the held half or better, the second half is drafted: the same raise, on exactly the products that were held back, with each price re-checked against today's cost. If it did not, the second half is never offered and the first event says why - "The raised half earned -6% against its hold-out, so the rest of the family was not offered." Rolling the first half back is one click, as always.
The held-back lines are the second half, which is why clearing the hold-out on a staged raise is not free: it raises the whole family at once, and there is no second decision left to make. The engine allows it - the operator decides - and records that the staging ended there.
Two halves, never three, and neither half is ever pushed by the engine. Both are drafts.
A cut that lost money is walked back
While raises are held, nothing in the engine would ever propose walking a price back up. So a cut that turned out wrong would stay on the shelf until somebody noticed. Two things notice it:
- At day 7 and day 14 a live cut is checked. If its products are selling no more units a day than before at the lower price (against the held-back products where it has them), the event says it is giving its discount away and can be rolled back from there. The verdict itself still waits for the full 28 days: most products sell about one a day, so a week's figures swing by more than a cut is expected to do. Only the case where the loss is near-certain is named early.
- When the 28 days close, Monday's job reads the cut once. If it lost gross profit, it drafts a walk-back: the lines that themselves lost, back at the prices they had before. A cut that earned keeps every line. The walk-back is a draft like any other and waits for a person.
A walk-back ignores the hold on raises on purpose. Putting back a price the store was charging a month ago, with its own sales history, is not a gamble on a new raise, and it never enters the engine's evidence as one. KVIs are never walked back this way: a KVI is priced to the market and judged on the baskets it brings in, not on its own margin.
A word on every price
The engine says something about every stocked product nightly, not only about the ones it proposed a move for. The word sits on the Products screen, filters like any other chip, and the product's own panel carries the figures behind it.
- Unpriced - no price on file, or no believable cost, so the margin is unknown. Nothing else can be said until that is fixed.
- Can't judge - under ten clean units in one of the two windows, too much of a window off the shelf, or a live price event already measuring the product. The honest non-answer, and the thing that keeps the other words worth reading.
- Slipping - gross profit per day on the shelf is down at least 15% against the fortnight before, while the rest of its shelf held; or its margin has fallen under the floor because a cost moved and the price did not. The shelf condition is what separates a product losing ground from a category losing ground, and the two have different answers.
- Stale - the price has not moved in six months and the family's own measured moves say a move of that size earns more, with a range that does not span zero. A price nobody has revisited is not automatically wrong; one the evidence points away from is worth an hour.
- Earning - the rest. Not praise: the absence of a finding.
Every figure behind it is counted the same way as everything else in the module - per day on the shelf, over weekday-aligned windows, on clean sales only - so the word and the numbers on the event screens cannot disagree.
Judged on the last complete day of your data, not on today. The window ends where sales and inventory snapshots both reach - the earlier of the two feeds' last day - because a window that runs on past the last sync compares real days against empty ones. When the data is more than three days behind, the badge says "judged on data through DATE" so a quiet word is not read as a fresh one.
A product that sold recently and has nothing left reads Off the shelf: its price is not selling anything, which is a different fact from "we have not looked yet".
The hub raises Slipping and never Stale. An unrevisited price is a question, and a hub that raises questions alongside alarms trains you to read neither.
Nothing reaches Dutchie without a push
No schedule writes a price. The Monday draft is a draft. A rollback is a push. The only writes are Push to Dutchie on a price event and Push price on a product page, both behind the Publish to Dutchie permission.
Where you see this
Pricing, Merit gaps, Events, Results, Setup.
Last checked against the product on 10 Oct 2026. This is the same article operators read inside BudLogix.