Technical report · Biryar, part 4 · Repricing

A Repricer That Knows Where It Stands

Every fifteen minutes Biryar decides the price of each product the business sells on Amazon. This report explains how that decision is made, why a learned pricing agent was taken out of charge after one day, and why the replacement asks what situation it is in before it asks what price to choose.

Loghman SamaniCiya LabsGermany, October 2026
Generated with an image model
Cold open · 6 September 2026

The first repricer in Biryar was a reinforcement-learning agent, a neural policy trained with Proximal Policy Optimisation [1] inside a simulated market. It went live on a Sunday morning with twenty-two listings to manage. This is what the logs record.

  1. Before launchThe simulator the agent trained in assumed thirty visitors an hour per product at a five percent conversion rate, thirty-six sales a day. The real listings were selling roughly ninety times less.
  2. The first ten hoursIn a market that busy, the profit-maximising move is to raise the price until demand visibly breaks. The agent did exactly that. Fifteen of the twenty-two prices went up and not one came down.
  3. The compoundingThe agent applied each change as a percentage of the current price, and the record of the current price was never refreshed. One listing went from €18.00 to €20.83 in ten hours of five percent steps.
  4. The echoAmazon takes ten minutes or more to publish a new price, and the agent ran every fifteen. Twelve prices were submitted three times each, because each pass still saw the old price and decided the same change again.
  5. 7 September, 00:15A new policy took over. It has made every pricing decision since, 58,098 of them by 4 October.

Nothing was wrong with the training. The agent learned the market it was shown with great fidelity. It was shown the wrong market, and it was asked to cover situations in which the information it relied on simply did not exist. The rest of this report is about the policy built from that lesson, which robotics researchers would recognise as a gap between simulation and reality [2], and which pricing researchers would recognise as the danger of optimising against a demand model that was never estimated from data [3].

58,098
pricing decisions recorded, 7 September to 4 October
91%
of decisions were to leave the price where it was
5%
the largest step any single decision may take
35% → 43%
share of checks in which the business held the Featured Offer, first week against the rest

The idea in one equation

On Amazon many sellers often offer the same product on the same page. Amazon places one of those offers behind the main purchase button, an arrangement it calls the Featured Offer and that sellers call the Buy Box. Most shoppers never look further, so the offer in that position takes almost all the sales and the others take very few. It is usually, but not always, the cheapest offer. Delivery speed, the seller's record and the fulfilment channel matter too, and algorithmic sellers win it more often than others [4].

That makes the economics of a price unusually clear. The expected profit of setting a price is the chance of holding the Featured Offer at that price, times the demand the listing has, times the margin the price leaves.

expected profit(p) = P(hold the Featured Offer | p, rivals)
× demand for the listing
× margin(p)
Demand is measured elsewhere in Biryar from sales history. Margin is arithmetic on fees and cost. Only the first term has to be learned from this account's own behaviour.

A good repricer therefore does three things. It never sets a price whose margin is negative. It moves the price in the direction that wins or keeps the Featured Offer when that is worth doing. And it does not move at all when it lacks the information to know which direction that is. The first version did none of the three reliably. The current one is built in eight layers, each answering one question, and a single fifteen-minute pass goes through all of them in order.

Layer 1Facts, and their wrong twins

A policy can only be as good as the facts it is handed. The most instructive bugs in this agent were all of one kind. A number was read from a source that looked right and described something slightly different. Table 1 lists the facts the policy sees and, beside each, the plausible source that turned out to be wrong.

FactRead fromThe wrong twinWhat the twin caused
Our current priceThe offer Amazon actually publishes to buyersThe price field of our own last submission"Twelve of twelve applied" while every buyer still saw the old prices
Competing offersEvery offer on the listing except our ownAll offers, ours includedThe agent reads its own price as the market and undercuts itself
Sales rateRecorded sales, with the length of history attachedA rate without its ageTwo days of silence treated as a measured zero
StockWhichever interface can see the fulfilment channelOne report for both channelsEvery warehouse-held listing read as empty
Floor and ceilingThe seller's own bounds, re-read every passA percentage of a base price the agent is itself movingA clamp that drifts with the thing it is meant to hold
Table 1. Each fact has a source that looks right and is not. When a fact cannot be read honestly the listing is skipped for that pass rather than given a plausible default.

The last sentence of the caption carries a design rule. When the agent cannot read a listing's published price or bounds, it does not fill the gap with yesterday's value or a sensible guess. It skips the listing until the next pass, because a guessed number handed to a pricing rule becomes a decision. The managed catalogue itself is kept equal to what the account actually lists. A scheduled job mirrors every active listing that carries a seller-set minimum and maximum, re-reads those bounds every six hours, and drops listings that have disappeared, so a new listing is priced within a quarter of an hour of its first units arriving rather than after someone remembers to register it.

Layer 2Naming the situation

The core insight of the redesign is that a listing can be in one of a few situations that do not share a signal, and that a single learned function was being asked to cover all of them. If competitors exist and someone else holds the Featured Offer, the price that matters is theirs. If we hold it, the price that matters is the next offer above ours, and the thing to protect is the position. If nobody else sells the product, there is no competitor price at all, and a rule built around competitor prices is choosing blind. And sometimes there is not enough information to act, in which case the right move is no move. The policy therefore decides the case first, explicitly, as Figure 1 shows.

Any competing offer? NO YES A week of sales historyand stock on hand? Do we hold the Featured Offer? NO YES UNKNOWNhold SOLE SELLERsteer by sell-through YES NO HOLDINGprobe up, stay under Are we already cheaper? YES NO OUTRANKEDprice is not the lever Fourteen quiet days, or would winningneed a price below the floor? YES NO CHASINGbut hold CHASINGundercut by one cent
Figure 1. The case is decided before any price is chosen. Each leaf defines the band a price may come from. Three of the six leaves keep the price where it is.

Chasing Someone else holds the Featured Offer and we are priced above it. The target is one cent below their price. A cent is enough to take the position and costs nothing extra, whereas a percentage would give away margin in proportion to the price for no additional chance of winning. Two conditions stop the chase. If winning would require a price below our floor, the offer is not winnable at a profit and the price holds. And if the listing has been watched for fourteen days without a single sale, winning the position on a product nobody buys would win nothing, so the price holds there too. Fourteen days, not one, because a newly listed product has no sales either, and cutting to win the position is exactly how a new listing gets its first.

Outranked We are already cheaper than the Featured Offer and still do not hold it. Being cheaper has failed to win it, so cutting further buys nothing. Amazon is choosing on something other than price. Raising towards the holder's price would capture margin, but only on the inference that price is irrelevant here, which has not been observed. By default the policy holds, and the option to raise exists but is off.

Holding We hold the Featured Offer. The policy probes upward, but never past one cent below the next offer above ours, because crossing it is how the position is lost. With no offer above at all, the ceiling and the step limit are the only bounds.

Sole seller Nobody else offers the product. There is no competitor price, so the policy steers by sell-through against a stock plan, the rate that would clear the units on hand in twelve weeks. Selling well above that rate leaves money on the table and the price rises. Selling well below it means stock will age, and the price falls. Within half the target either way the price holds, a deliberately wide dead band, because a sales rate measured on a slow catalogue is noisy and a controller that chases noise oscillates [5]. This is the only case in which a price rises on strength alone, and it requires at least a week of sales history.

Unknown Bounds are missing, stock is zero, competitors exist but none is priced, or there is too little history. The price holds and the reason is recorded.

Layer 3The envelope

Each case produces not a price but a band, the envelope, and a point inside it where the deterministic rule would sit. Every band is intersected with two limits that no case can widen. The price may not move more than five percent in one decision, and it may not leave the interval between the floor and the seller's ceiling. Figure 2 draws two envelopes on the same price axis.

floor €21ceiling €29 Chasing Featured Offer €23.10 rival €25.50 ours €24.00, moves to €23.09 Holding next offer up €24.60 rival €26.40 ours €24.00, band up to €24.59 €20€22€24€26€28€30
Our offerRival holding the Featured OfferFive percent step limitHolding band
Figure 2. Two envelopes with illustrative prices. Chasing moves to one cent below the Featured Offer because that target lies inside the step limit. Holding may rise only as far as one cent below the next offer, however much room the ceiling would allow.

The envelope is where learning is allowed back in. A learned component can choose where inside the band to sit, but whatever it returns is clamped into the band before use, so a model that has never seen a listing, or has gone wrong, costs accuracy and not money. The safe reinforcement learning literature calls this arrangement a shield, a fixed layer that sits between a learning agent and the world and blocks actions that would violate a specification [6, 7]. Here the specification is economic rather than physical, never below cost, never past the next rival when holding, never more than five percent at a time, and the shield is the part of the system that cannot be retrained into something else.

The five percent limit has a story of its own. The original agent applied changes as percentages of a price it believed was current. A clamp on the step size bounds the damage of any single bad decision and stops compounding, but as Layer 5 shows, it does not by itself bound a day.

A note on rival repricers Undercutting by a cent and then probing upward towards the next offer is a common pattern among algorithmic sellers, and two sellers following it against each other can produce saw-tooth price paths, a long slide followed by a jump back up. Economists know such paths as Edgeworth cycles and have shown they arise naturally in price-setting duopolies [8]. Recent work shows that learning pricing agents can also settle into prices above the competitive level without any communication [9]. The envelope's floor stops the slide at a profitable price, and the policy's records of rival prices over time will show whether such cycles occur on this account's listings. So far they have not been studied systematically.

Layer 4Floors that cannot be argued with

The floor is the one number in the system that no case, signal or model may cross. It is the higher of two values. The first is the seller's own minimum, set on each listing in Seller Central, which Amazon itself refuses to publish below, including from its own automated pricing tools. The second is the break-even price that Biryar computes.

landed cost = invoice cost × 1.19
fees(p) = referral rate × p + fixed fulfilment fee
break-even = (landed cost + fixed fee) ÷ (1 − referral rate)
The 1.19 is the VAT a business under the German small-business rule pays on its stock and cannot reclaim. Leaving it out inflates every margin by about a fifth, which is the size of error that turns a loss into an apparent profit.

The fee line is fitted from two live estimates of Amazon's fees at two different prices for the same product, rather than from a remembered percentage, because referral rates have lower bands for cheap items in some categories and fulfilment fees depend on each product's measured size. Two points determine a line exactly. When Amazon's fee service fails to answer, which it does unpredictably, a fee measured on an earlier run is preferred over a generic fallback, on the principle that a floor which has once been measured should not quietly become a guess. When a floor is written onto a listing, it sits three percent above break-even, so that a rounding difference or a fee revision cannot silently turn a break-even sale into a loss.

Cost is the one fact Amazon does not know. Invoices are read and keyed by barcode, the only identifier that appears on the invoice, the box and the listing alike. When the same product has been bought twice at different prices, the higher cost is used, because once both batches share a bin in Amazon's warehouse they are indistinguishable and a floor computed from the cheaper batch would sell the dearer one at a loss. Where no invoice is on file, the cost is derived from the seller's own floor minus estimated fees. That is an upper bound on what the goods cost, which errs in the safe direction, and the system records the derivation beside the number. Of the 26 listings currently managed, 22 carry such a derived cost, and for nine of them the computed break-even sits above the seller's own minimum, so the break-even is the floor that binds.

The floor is enforced in two places, deliberately. Amazon enforces the seller's minimum on its side, and Biryar refuses to submit any price below its recorded floor before the request is sent. A guard on one side only stops working the moment the other side changes.

Layer 5The day, not the step

Biryar's safety rules require the owner's approval for any repricer move larger than fifteen percent. That rule was written for an agent whose steps were unbounded. Once the envelope capped every step at five percent, a single decision could never reach fifteen, and the rule became unreachable. A lower threshold became a coin toss instead, because a clamped step lands exactly on its limit and rounding to the cent decided which side of the comparison it fell on. On the first live pass, nine nearly identical decisions were split between automatic submission and approval for no reason a reader could give.

What can actually run away is the day. Five percent a pass, ninety-six passes a day, is a price anywhere at all. So the approval gate measures how far each listing has moved from its first published price of the current day, however many steps it took. Once that drift reaches ten percent, the next move waits in the approval queue. Once it reaches fifteen percent, only the owner may approve it. The single-step thresholds remain beside the daily one, eight percent for an approval and fifteen for the owner. The five percent clamp makes them unreachable in normal operation, but they cost nothing and still bound a learned chooser that returns something wild.

Approvals behave like the rest of Biryar's approval queue, described in a later report. A pending price change carries its evidence, can be approved or rejected in the web interface, and expires if the market moves on before anyone decides. Since 7 September the gate has raised 233 price approvals. Thirty-four were approved, 68 rejected, one is pending and 130 expired unanswered, which is harmless by design, since an unapproved move simply does not happen.

Layer 6Writing a price without breaking it

Submitting a price to Amazon sounds like the simplest step. It turned out to hold the most dangerous bug in the project. The pricing bot shipped with a writer that replaced the whole offer attribute on a listing with a new value containing only the price. The seller's minimum and maximum live inside that same attribute, so the first successful write would have deleted both, on every listing, and left a percentage clamp computed from a moving base price as the only protection. The current writer reads the listing's existing offer, changes only the price, and sends everything else back exactly as it was. Whatever the seller set is preserved because it is never rebuilt from assumptions.

Three further guards sit in the writer. A price is verified only against the published offer, never against the submission, since reading back one's own submission always agrees with it. A listing whose last submission has not yet appeared, and is recent enough to still be in transit, is skipped on the next pass, which is what stopped the triple submissions of the cold open. And the target marketplace comes from configuration rather than from an environment value that, read naively, would have sent every change to the American marketplace instead of the German one. Some product types, two DVD listings among them, are rejected by Amazon as the envelope of a price update even though Amazon publishes definitions for them, and the writer reports those as refused rather than as submitted. Nineteen decisions so far have ended as refusals of one kind or another, each logged with its reason.

The live repricer runs with its dry-run setting switched off, on the owner's explicit instruction. Everything above is what makes that acceptable.

Layer 7Writing everything down

When the envelope policy first went live, it recorded its reasoning nowhere. The previous agent's loop had logged every decision, and the replacement loop did not inherit that, so for a short time the learning machinery described in Layer 8 had no input at all and the price-history chart in the web interface slowly emptied. A journal now records every decision, one row in the repricing table and one in the platform's prediction log, with the case, the band, the reason in plain words, the competitor prices, whether we held the Featured Offer, the sales rate and its age, and the cost the decision was made against.

Decisions to hold are recorded too. A policy that declines to move is making a claim, that the current price is right, and the holds are the control group against which any move means something. A training set made only of changes cannot answer whether doing nothing would have been better.

58,098 decisions Held, price left where it was52,750 · 90.8% Submitted to Amazon2,726 · 4.7% Queued for approval2,063 · 3.6% Queued for the owner540 · 0.9% Refused by the writer19 · 0.03%
Figure 3. What became of every decision since 7 September. Queued decisions repeat each pass until someone decides, so they outnumber the approval requests they belong to. Proposed moves averaged 4.8 percent in size, and 64 percent of them were increases.

By case, 39 percent of decisions found the account holding, 36 percent chasing, 20 percent outranked and 5 percent unknown. The sole seller case appeared four times. Every outranked decision held, as designed, and most chasing decisions held too, either because the step had already reached the target or because the floor or the quiet rule stopped the chase.

Layer 8Learning from the next scan

The obvious way to judge a price decision is by the sales that followed it, and the system still derives that label. Each decision is labelled once a week has passed with the units sold and the margin earned, judged against the cost the decision was made with rather than a cost learned later. But on this account the label is almost always zero. Fewer than one decision in two thousand was followed by a sale within the window. A model fitted to that learns to predict zero, and a policy trained on it learns nothing about price.

The same records carry a second outcome that is neither rare nor slow. Every decision records whether we held the Featured Offer, and the next scan of the same listing, fifteen minutes later, records whether we hold it then. Pairing the two grades each decision by its consequence. If this price is published now, will we hold the Featured Offer at the next observation? The answer is positive in about 42 percent of cases, it resolves in minutes, and by the equation at the start of this report it is precisely the term that has to be learned from the account's own behaviour.

A gradient-boosted classifier [10] is trained on these pairs to predict that probability from our price relative to the cheapest rival and to the current holder, the gap in euros, the number of rivals and how many undercut us, whether we already held the position, our price relative to our floor, stock, and the hour of day. Two choices follow the platform's rules for any probability that will be multiplied by money. Validation is temporal, four folds that always train on the past and score on the future, since a random split would let the model see tomorrow's market while being graded on today's. And calibration is reported beside discrimination, because a model that ranks correctly while claiming ninety percent for a sixty percent event would price with false confidence.

Price features only0.920 Previous holder only0.944 Full model0.991 00.20.40.60.81 dashed line, base rate 0.42, what guessing scores
Figure 4. Area under the precision-recall curve for predicting the Featured Offer at the next scan, cross-validated over four time-ordered folds on 35,975 labelled decisions. The full model scores 0.991 with a spread of 0.007 across folds, a Brier score of 0.024 and a calibration error of 0.017. Precision-recall area is reported because it is more honest than the more common ROC area when the classes are unequal [11].

The two baselines in Figure 4 say what the model has actually learned. Whether we held the position fifteen minutes ago is by itself an excellent predictor, because the Featured Offer is sticky. Price features alone do almost as well. The model combines both and improves on each. Including the previous holder was a deliberate choice. The policy knows it at decision time, and hiding a legitimate predictor to make the price effect look larger would be fitting the story rather than the market.

The model's honest limit is stated in its own documentation. The training data come from a deterministic rule whose price is almost a function of the rival's price, so price and rival price are close to collinear, and the model is only valid on the combinations that rule actually visited. It predicts well and cannot answer what would happen at a price two cents lower outside that support, which in causal-inference terms is a failure of positivity [12]. More months of the same data will not fix that. Deliberate variation will, a small randomised jitter inside the band the envelope already computes, which is the classic remedy for learning demand while pricing [3, 13].

The model ships in shadow. It runs beside the rule, its predictions are logged, and it changes no price. Promotion to a role in pricing is, like every model in Biryar, the owner's decision after the shadow has shown it would have done better, and the report on model operations describes how that comparison works.

What happened to the original agent The learned agent was not discarded. Its logged decisions are used where they are unambiguously valid, to recalibrate the simulator's demand curve from observed sales. Feeding logged decisions straight into the agent's training as though they were fresh experience would be the standard error of offline reinforcement learning, training a policy on data produced by a different policy [14], and it fails silently because it still produces a model. Fitting the demand curve first and retraining inside the corrected simulator keeps the learning on-policy. On the first 902 labelled decisions the fitted demand level came out a small fraction of what the simulator had assumed, which is the cold open restated as a number.

What the numbers show

The figure that matters most for the business is how often its offers hold the Featured Offer, since that is where the sales are. Figure 5 shows the daily share of checks in which the account held it.

55%45%35%25% first week, mean 35% since, mean 43% 7 Sep14 Sep21 Sep28 Sep4 Oct
Figure 5. Share of each day's checks in which the account held the Featured Offer. The rise after the first week coincides with the policy's first full week of operation, but it is not a controlled comparison. The catalogue grew from 22 to 28 listings over the period, and rivals came and went.

The honest reading is modest. The share rose from about a third to over forty percent and has stayed there. The policy that produced that change never once crossed a floor, never moved a price more than five percent in a step, and asked for permission whenever a day's drift exceeded its threshold. Whether the rise was caused by the policy or by changes in the catalogue and the competition cannot be separated from these data alone, which is one more argument for the deliberate price variation described in Layer 8. What the data do show clearly is the absence of the failure that started this report. There has been no runaway price since 7 September.

The Repricing page of Biryar
Figure 6. The Repricing page on 4 October. Across the top, whether the scan is running, how many listings hold the Featured Offer and how many sit at their floor or ceiling. Below, a move waiting for approval, a five percent probe while holding the Featured Offer, with its reason in plain words. At the bottom, every managed listing with its price, floor, ceiling, position inside its band, who holds the Featured Offer and which case it is in. Product names and SKUs are blurred.

Open questions

References

  1. Schulman, J., Wolski, F., Dhariwal, P., Radford, A., and Klimov, O. (2017). Proximal policy optimization algorithms. arXiv preprint arXiv:1707.06347.
  2. Jakobi, N., Husbands, P., and Harvey, I. (1995). Noise and the reality gap. The use of simulation in evolutionary robotics. In Advances in Artificial Life, ECAL 1995, Lecture Notes in Computer Science 929, 704–720. Springer.
  3. den Boer, A. V. (2015). Dynamic pricing and learning. Historical origins, current research, and new directions. Surveys in Operations Research and Management Science, 20(1), 1–18.
  4. Chen, L., Mislove, A., and Wilson, C. (2016). An empirical analysis of algorithmic pricing on Amazon Marketplace. Proceedings of the 25th International Conference on World Wide Web, 1339–1349.
  5. Åström, K. J., and Murray, R. M. (2008). Feedback Systems. An Introduction for Scientists and Engineers. Princeton University Press.
  6. Alshiekh, M., Bloem, R., Ehlers, R., Könighofer, B., Niekum, S., and Topcu, U. (2018). Safe reinforcement learning via shielding. Proceedings of the AAAI Conference on Artificial Intelligence, 32(1), 2669–2678.
  7. García, J., and Fernández, F. (2015). A comprehensive survey on safe reinforcement learning. Journal of Machine Learning Research, 16, 1437–1480.
  8. Maskin, E., and Tirole, J. (1988). A theory of dynamic oligopoly, II. Price competition, kinked demand curves, and Edgeworth cycles. Econometrica, 56(3), 571–599.
  9. Calvano, E., Calzolari, G., Denicolò, V., and Pastorello, S. (2020). Artificial intelligence, algorithmic pricing, and collusion. American Economic Review, 110(10), 3267–3297.
  10. Friedman, J. H. (2001). Greedy function approximation. A gradient boosting machine. Annals of Statistics, 29(5), 1189–1232.
  11. Saito, T., and Rehmsmeier, M. (2015). The precision-recall plot is more informative than the ROC plot when evaluating binary classifiers on imbalanced datasets. PLoS ONE, 10(3), e0118432.
  12. Hernán, M. A., and Robins, J. M. (2020). Causal Inference. What If. Chapman and Hall/CRC.
  13. Misra, K., Schwartz, E. M., and Abernethy, J. (2019). Dynamic online pricing with incomplete information using multiarmed bandit experiments. Marketing Science, 38(2), 226–252.
  14. Levine, S., Kumar, A., Tucker, G., and Fu, J. (2020). Offline reinforcement learning. Tutorial, review, and perspectives on open problems. arXiv preprint arXiv:2005.01643.

Written from the live system's databases, configuration and design notes. Counts were read from the production server on 4 October 2026. Product and listing names are withheld. Biryar is a Ciya Labs project.