Turning Slow Stock Back into Cash
Excess inventory is cash you can't spend. Shopify markdowns graded by ABC analysis on every channel cut a UK retailer's unsold stock from 12% to 4.8%.
Case Study Highlights
Systems: A UK health and beauty retailer’s Shopify store, one of several channels sharing its stock, and a markdown engine that sets its prices
Problem: Markdowns were decided by hand, on whatever looked overstocked or close to its expiry date. Many products were missed, and the cash in their excess stock stayed tied up on the shelf
Solution: Every product is graded each night on its recent revenue across every channel, and the grade sets how much stock it’s allowed to hold. Several times a day the engine checks every product against that limit and marks down in proportion to the excess. Dated batches are counted down from sales and marked down in steps as their date approaches
Result: The share of stock value sitting unsold for 90 days fell by 60%, from about 12% to an average of 4.8% in the second half of 2025. In the same six months, automatic markdowns reached more than 700 products, and the whole discount on every marked-down unit came to under 1% of sales
Every catalog has stock that isn’t moving: bought on a forecast that didn’t happen, to meet a supplier minimum, or on a good feeling. Until it sells, the cash that paid for it can’t fund reorders of what is selling, and some of it is running down to an expiry date.
Marking it down is the obvious fix. Doing it across thousands of products, early, while a small markdown still works, is the difficult bit.
That’s the job we built for the same UK health and beauty retailer whose profit per order we wrote about previously.
Markdowns by hand
Before this existed, markdowns were done by hand. Someone spotted a product that looked overstocked, or a batch getting close to its date, and reduced it.
That works for the products people look at. A catalog of thousands doesn’t get looked at evenly, though. Bestsellers get attention because they’re the ones that run out, and the tail gets looked at when someone happens to scroll past it. Here, many products were simply missed.
Each one is a missed opportunity, and the cost grows the longer it sits. Excess stock that would have cleared with a small markdown early on waits until it needs a big one, or until the date on the label decides for you.
The job itself is a person carrying information from the stock system to the price, by eye, which is what our manifesto calls human middleware.
Days of cover
“Overstocked” is a feeling. The engine needs a number.
The number is days of cover: how long the stock on hand would last at the rate it’s been selling, counted across every channel. The stock is shared, so the sales have to be too.
Picture the same pile of 200 units on two products. One sells a hundred a week. The other sold three last month.
On a stock report they look identical. In days of cover, one has two weeks and the other has about five and a half years.
Say each unit cost £4. That’s £800 of cash in both piles. One pile hands its £800 back in two weeks. The other hands it back over five and a half years, a few pounds at a time.
The grade sets the target
When cash is tight, the obvious place to look is the stock report, sorted by value. The products at the top have the most money in them, so surely that’s where the prices should come down.
It’s the wrong list.
The biggest stock values usually belong to the best sellers, because a business stocks most of what sells most. That stock is moving. Mark it down and you give away margin on units that would have sold at full price anyway, and you can run a bestseller out of stock before the next delivery lands.
The stock that overstays is usually at the other end, in the slow sellers. Whatever was bought for them takes the longest to come back as cash. A modest pile of a slow seller can be years of supply, even though it never looks big on a stock report.
That end of the catalog is what ABC analysis finds. Rank every product by revenue and split the list the classic way: the products making up the first 80% of revenue are A, the next 15% are B, and the rest are C.
Shopify will grade your products for you, to be fair. Its ABC analysis report does exactly that. But it can only count Shopify’s own orders. This retailer also sells on Amazon, eBay, TikTok Shop and more, out of the same stock, and none of those orders are in Shopify. A product that sells steadily on Amazon and rarely on the website lands in C, a grade it hasn’t earned.
And even a correct grade is only a list. The report doesn’t act on it, so the decisions are still yours, one product at a time.
So every night, the engine grades every product itself, on the revenue it brought in over a recent window across every channel the retailer sells on. Anything that sold nothing at all is a C. The grade is written back onto the product in Shopify as a tag, where anyone on the team can see it.
This grade gets acted on. It tells the engine which products have earned the right to carry stock, by setting how much cover each one may hold before anything happens to its price.
The engine never looks at stock value at all. It looks at cover, measured against the target for the product’s grade. A and B products get a lot more room before anything happens. C products get the tightest target, because the tail is where stock goes to wait.
The excess is measured against the target itself, too, so the same few extra weeks of stock counts for much less on a bestseller than on a slow seller.
Margin doesn’t come into the grade, so a high-revenue product on a thin margin still grades A. What a markdown costs shows up separately, order by order, in the retailer’s order-level P&L.
The markdown
Several times a day, the engine works out each product’s cover and compares it with the target for its grade.
A product a little over its target is left alone. Past that, the markdown deepens the further over target the product is, in a few fixed steps.
When cover drops back under target, the price goes back to what it was before the markdown.
When the history lies
Days of cover is only as good as the sales figure underneath it, and there are situations where that figure is wrong in a perfectly predictable way.
A product that was recently out of stock has terrible sales, because for part of the window there was nothing to sell. Divide its stock by those sales and the engine sees a product drowning in cover, right after it was empty.
The day a delivery lands, stock jumps and sales haven’t caught up. Cover spikes on a product that’s selling perfectly well.
A new product has no sales history at all, which works out as infinite cover. An engine that took that literally would mark down every new arrival on its first day.
In each case the engine refuses to act, and the price stays put until the history means something again.
Refusing to act is most of what makes automated pricing safe to leave running. Writing the rule that marks things down is easy. The care goes into the list of times it shouldn’t.
That list goes on. The cover rule never touches:
- the last few units of anything
- the cheapest items in the range
- bundles
- anything a person has tagged as not to be discounted
- wholesale variants
A simple forecast, on purpose
Cover divides stock by expected sales, and we kept that estimate simple: a linear forecast from recent sales with an adjustment for seasonality. The engine only has to judge whether a product holds more stock than its grade allows, and a simple estimate does that well. A full demand forecast is a project of its own, and leaving it out kept the cost down.
A proper one reads a calendar with layers, from Christmas down to payday. It separates real demand from the extra sales a markdown creates, and estimates what would have sold during a stockout. The factors interact, too: payday in the week before Christmas isn’t the two effects added together. Each one needs its own data and careful modeling, and the list never ends.
If the client ever wants one, it plugs straight in. A better forecast replaces the expected-sales figure and leaves the grades and targets as they are.
Real prices, not discount codes
When the engine marks something down, it changes the product’s actual price in Shopify. The marked-down figure shows everywhere the price shows, cart included.
A discount code can’t do this job. It only reaches shoppers who already have it, and slow stock needs a lower price every shopper sees while browsing. Even an automatic discount only shows as a crossed-out saving on product and collection pages with a third-party app, as Shopify’s help page on sale prices spells out.
Each run sends its price changes to Shopify as one bulk job, and every product with a markdown is tagged as on sale. The tag comes off again when the price goes back.
Before it changes anything, each run saves a copy of every price it’s about to touch. If a run ever goes wrong, the previous prices are a file away.
And pushing prices to Shopify takes a while, long enough for someone on the team to change a rule on the screen while a run is still going. So the run keeps their change instead of writing over it, and the next run acts on it.
Stock with an expiry date
A lot of what this retailer sells has a shelf life, and that’s where the cash case is sharpest. Slow stock can still get its money back eventually. Once a batch passes its date, it may not get it back at all.
Cover can’t see a date. A product can sit inside its target and still hold a batch that won’t sell through in time, so dated stock has its own rule.
Shopify has no expiry date field, and the same product can arrive in two deliveries with different dates. So the dates live on a screen of their own, which lists every product with stock so none gets skipped because nobody thought to add it. Products that never carry a date can be hidden from it.
The engine counts each batch down from sales twice a day, with a three-pack counting as three units. It assumes the dated batch ships first, so the count is an estimate that a recount corrects.
The markdown deepens in steps as the date gets closer. It runs on the date alone, so even a batch that would have cleared on its own gets marked down.
Once the countdown says the old batch has gone, the price goes back up, to full or to the step the next batch’s date calls for.
A weekly email lists every batch close to or past its date that still has units left.
The screen
Every rule the engine follows can be changed by the client’s own team, product by product, without calling us.
Most products follow the cover rule. Any one of them can be switched to something else:
- clear it out, at the deepest step until it’s gone
- a set percentage off, or a set price
- follow its expiry batches
- leave it alone
Targets can be moved for a whole grade or a whole brand at once. When a product changes grade, its target moves with it, unless someone has pinned it.
And markdowns can be switched off for an entire grade with a single switch, which leaves any percentage or price a person set by hand untouched.
What actually changed
In the year before the engine went live, about 12% of the store’s stock value was slow stock that hadn’t sold in 90 days. By the second half of 2025, slow stock averaged 4.8%, and more than 95% was moving stock, in products that had sold in the last 90 days.
Over those six months, the automatic markdowns reached more than 700 products, and nobody had to spot a single one. They cost under 1% of sales, counting the whole discount on every marked-down unit, even the ones that would have sold at full price anyway.
Every product in the store is now checked against its own target several times a day, so excess stock gets a small markdown before it ever needs a big one.
What’s left for a person is the part that needs judgment, like the seasonal line or the brand worth protecting.
Frequently Asked Questions – Automated Markdowns
- What is ABC inventory analysis?
- A way of ranking products by their share of revenue. Sort every product from the highest earner to the lowest and add up as you go: the products making up the first 80% of revenue are A, the next 15% are B, and the rest are C. Shopify uses the same split in its own ABC report. It ranks on revenue alone, so a high-revenue product on a thin margin still grades A. In this system the grade decides how much stock each product may hold before a markdown starts.
- Why not use Shopify's own ABC analysis report?
- It only counts orders placed through Shopify. If you also sell on Amazon, eBay or TikTok Shop from the same stock, a product that sells steadily there but rarely on your website lands in C. This engine grades on revenue from every channel, so the grade matches what the product actually earns. The report also stops at the grade: it doesn't change any prices, so every markdown is still a decision made by hand.
- Won't it mark down my bestsellers when they're carrying a lot of stock?
- No. The engine judges every product on cover measured against a target set by its grade, and stock value doesn't come into it. Bestsellers usually hold the most stock by value because they sell the most, and that stock is moving. A and B products get far more room before any markdown starts, and the excess is measured relative to that larger target, so the same extra stock counts for much less on a bestseller. The slow C-grade products, where stock overstays, get the tightest target.
- What is stock cover, or days of cover?
- How long the stock you have would last at the rate it's been selling, on every channel it sells on. Forty units selling ten a month is four months of cover. It's a better signal than the stock level, because the same 200 units can be two weeks' supply of one product and years of another. The engine compares each product's cover with the target for its grade, and marks down only what sits over it.
- What's the difference between a markdown and a discount code?
- A markdown changes the price itself, so the lower figure shows everywhere the price shows, cart included. A discount code or automatic discount is applied at checkout, and Shopify only shows its saving as a crossed-out price on product and collection pages if you add a third-party app. This engine writes real prices, and puts the original price back when the markdown ends.
- What stops it marking down a product that's just had one bad month?
- Several things. A product only a little over its target is left alone, and the markdown steps up gradually with the size of the excess. The engine also refuses to act when the sales history can't be trusted, such as straight after a stockout or a big delivery. New products are left alone until they have a pattern. Bundles and the last few units of anything are never touched by the cover rule at all.
- How does it handle stock that's close to its expiry date?
- Through a separate rule, because stock cover can't see a date. Each dated batch is entered on its own screen with its quantity and date, and the engine counts it down from actual sales twice a day, with packs counting as several units. As the date approaches the markdown deepens in steps, and once the batch has sold through, the product goes back to full price. A weekly email lists every batch that still has units left and is close to its date or past it.
- Can we override it?
- At every level. Any single product can be switched from the cover rule to a set percentage, a set price, a clear-out, an expiry rule, or left alone. Targets can be changed for a whole grade or a whole brand in one go, and markdowns can be switched off for an entire grade without touching the prices people set by hand. None of it needs us.
- Does it check the margin before marking something down?
- No. It decides on stock and sales alone. Markdowns come in a few fixed steps, with the deepest kept for heavy overstock and stock close to its date. What each marked-down order made is visible in the retailer's order-level P&L the same day, which is where a markdown that costs more than it clears would show up.
- Does it forecast demand, or handle seasonal products?
- Simply, and on purpose. Cover comes from a linear forecast of recent sales with a seasonality adjustment, which is a good estimate for deciding whether a product holds more stock than its grade should carry. Anything it can't anticipate is handled by a person setting that product to be left alone. A full forecasting model, with promotions and lost sales built in, is a separate project, and it would slot into the same rule because cover is just stock divided by expected sales.
- Does this need Shopify Plus?
- No. Nothing in it depends on the plan. It reads products and variants and writes prices and tags through Shopify's Admin API, which every plan exposes. The difficulty comes from how cleanly your stock and sales data is recorded.
- How long does something like this take to build?
- It depends mostly on your data. Grading needs sales history that maps cleanly onto products, and cover needs stock figures you trust. If both are already in place the engine itself moves quickly. If they aren't, that work comes first. We start with a discovery phase to see what's recorded, and quote against that instead of a guess.
Selling on several channels from one pool of stock, with cash stuck at the slow end of the catalog? Drop us a message and we’ll work out how much of it is worth freeing up. No pitch, just a conversation.
You can also see more of our work or read our manifesto to understand how we approach these problems.