The first version of the optimizer did one thing: undercut the cheapest competitor by 5%. It worked for products where the seller wasn't competitive. But for products where the seller was already ranked first, the algorithm dropped the price by 5% below itself. Every run made the product cheaper. Left unattended for a week, a €450 inverter would have been listed at €320. The algorithm was solving for a rank it had already won.
The fix was to split the price optimizer into two branches: one for when you're losing, one for when you're winning. The winning branch does the opposite of undercutting. It tries to raise the price.
The Rank 1 Problem
On Idealo, the cheapest offer gets the default "Buy" button. Every other seller gets a smaller listing below it. The difference between rank 1 and rank 2 is not 10% fewer clicks. It's closer to 70% fewer clicks, because most comparison shoppers click the first price they see and move on.
So rank 1 is worth defending. But most pricing algorithms treat "be cheapest" as the only objective. They don't have a concept of "I'm already cheapest, now what?" The naive answer is "keep the price the same." The better answer is "find the maximum price that still keeps rank 1."
That gap between the seller's current price and the second-cheapest competitor is where the margin lives. If a seller is listed at €399 and the second-cheapest is at €419, there's €20 of price headroom. The algorithm should capture as much of that gap as possible without losing rank 1.
How the Two Branches Work
Both branches live in calculate_optimized_price_with_ranking(). The function takes the current selling price, a list of competitor prices, the seller's shop rating, number of ratings, delivery speed, shipping cost, and a desired margin floor (default 15%).
The first check is initial_rank == 1. If true, the algorithm enters the rank-defense branch. If false, it enters the undercut branch.
The undercut branch is simple:
undercut_percentage = Decimal("0.05")
desired_price = lowest_price * (Decimal("1") - undercut_percentage)
desired_price = max(desired_price, min_required_price)
Drop to 5% below the cheapest competitor, but never below the cost floor. Cost price is calculated as 80% of the current selling price, and the minimum required price is the cost divided by (1 minus the desired margin). At 15% margin, a product with €400 selling price has a cost of €320 and a floor of €376.47. If undercutting would push below that floor, the algorithm holds at the floor and accepts a lower rank.
The rank-defense branch is more involved. It calculates a max_allowable_price: the second-cheapest competitor's price minus €0.05 (a safety buffer). Then it applies two adjustments.
Delivery and Reputation Adjustments
Delivery speed adjusts the price in percentage terms:
if delivery_days <= 3:
delivery_adjustment = Decimal("0.03") # +3%
elif delivery_days <= 7:
delivery_adjustment = Decimal("0.02") # +2%
elif delivery_days > 14:
delivery_adjustment = Decimal("-0.04") # -4%
else:
delivery_adjustment = Decimal("-0.02") # -2%
Fast delivery earns a premium because Idealo shoppers weigh delivery time against price. A seller who delivers in 2 days at €409 is more attractive than one who delivers in 16 days at €399. The adjustments aren't derived from Idealo's ranking algorithm (which isn't public). They're conservative estimates based on conversion rate patterns across the client's product categories.
The shop reputation adjustment combines two signals: shop rating (out of 5) and total number of reviews. Rating gets 60% weight, review count gets 40%. Both are normalized to a 0-1 scale (rating normalized to 1-5 range, reviews capped at 5,000). The maximum combined premium is 10%.
A seller with 4.8 stars and 3,200 reviews gets a larger price increase than one with 3.5 stars and 150 reviews. The premium reflects the conversion advantage of social proof: shoppers on Idealo compare not just price but seller trustworthiness before clicking through.
The final price is the minimum of the potential adjusted price and the max allowable price (second-cheapest minus €0.05). Then it's clamped above the margin floor. The result is the highest defensible price that maintains rank 1.
What I Got Wrong About Shipping Cost
The optimizer returns an adjusted shipping cost alongside the optimized price. The original intent was to redistribute the price change into shipping cost, so the seller could lower the visible product price and hide part of the cost in shipping. Idealo sorts by total price (product + shipping), so this doesn't affect ranking.
The problem was in cost_of_shipping. The line cost_of_shipping = shipping_cost - selling_price computes the difference between what the customer pays for shipping and the product price. When shipping is €5.99 and the product is €399, the "cost" is -€393. The adjusted_shipping_cost becomes desired_price + (-393), which is a large positive number that doesn't correspond to any real shipping cost.
The calculation works mathematically as a total-price redistribution, but the output is confusing to read in the API response. A seller sees adjusted_shipping_cost: 12.50 and assumes the algorithm is recommending they charge €12.50 for shipping, when the actual recommendation is about total price positioning. I'd redesign this as two separate outputs: recommended_total_price and recommended_price_shipping_split, making the intent clear.
The Decimal Precision Decision
All price arithmetic uses Python's Decimal type with precision 10. This wasn't the original implementation. The first version used floats, and rounding errors were invisible until the test suite compared outputs.
A float calculation of 399.99 * 0.95 returns 379.9905000000001. Rounded to 2 decimal places, that's €379.99. The correct answer is €379.99. So the error was invisible in this case. But Decimal("399.99") * Decimal("0.95") returns 379.9905, which rounds to the same €379.99. The difference shows up at different price points and with chained operations. When the delivery adjustment and rating adjustment are applied sequentially, float imprecision compounds. On a €2,800 product with a 3% delivery premium and a 7% reputation premium, the float path and the Decimal path diverge by €0.03. Small enough to ignore for one product. Multiplied across 200 products repriced daily, that's pricing inconsistency the seller notices.
The getcontext().prec = 10 at the top of the optimizer sets global decimal precision. Ten digits handles any product price below €10 million with 2 decimal places, which covers every product category on Idealo.
Where the Algorithm Falls Short
The 5% undercut is a fixed constant. For a €15 phone case, a 5% undercut is €0.75, which might not be enough to visibly change rank. For a €3,500 solar inverter, a 5% undercut is €175, which is aggressive and might trigger a price war. The undercut percentage should scale inversely with price, or the seller should be able to set a per-category undercut strategy.
The cost price calculation assumes 80% of selling price. That's a placeholder, not a real cost input. The system has no supplier integration or cost-of-goods data. It estimates cost from the current selling price, which means the margin floor drifts: if the seller manually raises the price, the system assumes cost went up too, and the floor moves. A proper cost input (from an ERP system or manually uploaded) would fix the drift, but the client didn't have a structured cost feed when this was built.
The optimizer also doesn't account for product-specific conversion rates. A product with a 40% click-to-purchase rate at rank 1 should be priced differently from one with a 5% rate. Without clickstream data from the seller's analytics, the algorithm treats all products as equally elastic. Adding a conversion weight would require connecting Idealo's click-through data, which the Business API's offer report doesn't include.