Back to Homepage

The pricing engine that retired Monday's spreadsheet

Every Monday morning, a developer at a B2B marketplace opened a shared spreadsheet, copied the week's price multipliers into a management command and ran it against production. Two hundred listing categories depended on that ritual. It kept prices three to seven days stale, and once it put premium listings online at ten times their price.

Services

Backend Engineering, Product

Industry

B2B Marketplace

Year

2023-2024

THE CHALLENGE

Six hours at ten times the price

The spreadsheet was the pricing system. Account managers typed multipliers into it during the week; the developer imported them on Monday. Nobody could see what a change would do before it was live, and typos went straight to buyers. The worst case was a seasonal multiplier entered as 12.5 instead of 1.25: premium listings sat at ten times their intended price for six hours before anyone noticed. Between incidents the process was merely slow: a market that moved on Tuesday was priced the following Monday.

THE SOLUTION

Rules as data, evaluated in order

The engine treats pricing rules as data. Each rule is a database record with a priority and a handler class whose apply() receives the current price and the market context and returns a new price. Account managers edit them in Django admin: a demand threshold, a supply floor, a seasonal modifier. A published change is live immediately, with no deploy. That freedom is also the risk: a rule set nobody reviews can do the same damage as the spreadsheet, faster, which is why the engine ships with a preview mode, price bounds per category and an evaluation log. A third-party pricing product was rejected: those tools are built for millions of SKUs and competitor price scraping, while this marketplace prices 200 categories on regional demand patterns, equipment condition grades and seasonal industry cycles that no off-the-shelf tool models.

The pipeline. Each rule sees the previous rule's output; clamp and log come last:

Python
class PricingEngine:
    def evaluate(self, listing, context):
        """Evaluate all active rules in priority order."""
        price = listing.base_price
        log = []

        rules = PricingRule.objects.filter(
            is_active=True,
            category=listing.category,
        ).order_by('priority')

        for rule in rules:
            handler = rule.get_handler()
            old_price = price
            price = handler.apply(price, context)
            log.append({
                'rule': rule.name,
                'input': old_price,
                'output': price,
                'delta': price - old_price,
            })

        # Clamp to category bounds
        price = max(listing.category.price_floor, price)
        price = min(listing.category.price_ceiling, price)

        PriceEvaluation.objects.create(
            listing=listing,
            base_price=listing.base_price,
            final_price=price,
            rules_applied=log,
            context=context,
        )
        return price

Preview a rule change

The draft opens with the peak markup raised from 25 % to 40 %. Compare the columns, then publish or discard. Push demand up and supply down until the ceiling clamps the draft, and watch the composed adjustment outgrow the largest single rule.

Rule engineUnpublished changes

Market context

85%
50%

Evaluation pipeline

RulePublishedDraft
Base price€120.00€120.00
Demand surge+1 % per demand point above the threshold70% ×1.15€138.00×1.15€138.00
Low inventory+1 % per supply point below the floor30% ×1.00€138.00×1.00€138.00
Seasonal modifierPeak season only. Off-peak is fixed at -15 %40% (Published: 25%) ×1.25€172.50×1.40€193.20
Clamp€60.00 - €300.00×1.00€172.50×1.00€193.20
Final price€172.50+12%€193.20

Price across the demand range

Price across the demand rangeFinal price for every demand level at the current supply and season. Dashed line: published rule set. Solid line: draft. Published: €150.00 - €195.00. Draft: €168.00 - €218.40.
+61%Composed adjustment
+40%Largest single rule
2Evaluations logged
0Rule sets published

A rule handler. The threshold and the rate are configuration, the math is code:

Python
class DemandSurgeRule(BasePricingRule):
    """Increase price when demand exceeds threshold."""

    def apply(self, price, context):
        demand = context.get('demand_percentile', 50)
        if demand > self.config['threshold']:
            surplus = demand - self.config['threshold']
            multiplier = 1 + (surplus * self.config['rate'])
            return round(price * multiplier, 2)
        return price
Safeguards

When a rule is wrong

Preview mode. A rule in preview is evaluated on every computation and logged, but the live price does not change; the admin shows live and projected price side by side, per listing. 'What will this rule actually do' is answered before publishing.

Bounds. Every category has a floor and a ceiling, applied once after the last rule, so no combination of rules can push a price outside them. Three rules compounding to a 200 % markup still stop at the ceiling; an off-peak discount never crosses the floor into negative margin.

The log. Each computation is stored as a PriceEvaluation record: listing, base price, every rule that fired with its own delta, final price. When a buyer questions a price, support reads that record instead of reconstructing the week's rules.

THE RESULT

What changed on Monday morning

Nothing happens on Monday morning now. Account managers edit rules when the market moves, preview them against live prices and publish the same day. In the first six months, preview mode stopped 14 misconfigured rules before they reached buyers. The spreadsheet era produced roughly one pricing incident a month; there has been none since launch. Support settles a disputed price from the evaluation log in about three minutes instead of 45. Revenue per listing rose in the first quarter after launch, and the engine now prices 200 categories without anyone copying a number.

KEY METRICS

+18%Revenue per listing, first quarter after launch
0Pricing incidents since launch
50kPrice evaluations per day
CLIENT FEEDBACK

"I change a rule in the admin, check the preview next to the live price, and publish. Nobody waits for Monday any more, and I have not asked a developer to run an import since the launch."

Head of Sales

B2B Marketplace · Commercial

FOR YOUR PROJECT

  • When it applies

    Prices change on a schedule or by hand, and the people who own them cannot ship a change without a developer. The same shape fits fees, commissions and discounts.

  • What to check

    How a price is computed today. If two adjustments can apply at once, decide whether they stack or override, and write that down before any code exists.

  • What it needs

    A rules table with a priority field, an admin to edit it, a preview path and an evaluation log. Here that was Django and PostgreSQL, no new infrastructure, as a backend and product engagement over 2023-2024.

FAQ

TECHNOLOGY STACK

Django
Python
PostgreSQL

Daniel made a decisive contribution to the founding and implementation of the companies Solvster and VIARES. He was responsible for the entire technical setup of the online presence, including server setups, developing and optimizing the website, and ensuring functionality. I was particularly impressed by how proactively and solution-oriented he always worked on improvements. Daniel was always very responsive and reliable - it was a pleasure working with him. His expertise and commitment contributed significantly to the success of both projects!

Dietmar Eglhofer - CEO, Solvster & VIARES

Follow in the footsteps of Dietmar and bring your vision to life.

Get In Touch