Dynamische Preisgestaltung mit KI: von der Elastizität zu den Leitplanken

Kurz gesagt

Niveau: Fortgeschritten

Dynamische Preise sind Elastizität plus Leitplanken, im Rahmen des deutschen Rechts.

  • Elastizität sagt, wie stark sich die Nachfrage bewegt, wenn sich der Preis bewegt, und sie ist von Produkt zu Produkt verschieden.
  • Die Aufschlagsregel setzt den Preis aus Kosten und Elastizität, und eine unscharfe Elastizität heißt ein unscharfer Preis.
  • Ein Nachfragemodell sagt die Stückzahl bei Kandidatenpreisen voraus, dann nimmst du den besten Deckungsbeitrag.
  • Leitplanken: große Vorschläge prüfen, bevor begrenzt wird, eine echte Untergrenze für die Marge, kleine Schritte, eine Sperrfrist.
  • Deutsche Regeln: Gesamtpreis mit Versandkosten, keine falsche Eile, kein Preiswechsel im Checkout, Streichpreise nur mit dem Tiefstpreis der letzten 30 Tage und nie Preise mit Mitbewerbern absprechen.

Dynamische Preisgestaltung heißt nicht, Preise wild zu verändern. Sie heißt zu wissen, wie empfindlich die Nachfrage jedes Produkts ist, den Preis zu setzen, der sich rechnet, und einen Algorithmus in Regeln zu halten. Ich gehe Elastizität, Aufschlagsregel, Nachfragemodell und Leitplanken an einem erfundenen Kölner Shop für Kochgeschirr durch, mit den deutschen Regeln zu Preisangaben und Wettbewerb, die die Maschine einhalten muss.

Dynamische Preise sind Elastizität plus Leitplanken

Die meisten Artikel über dynamische Preisgestaltung beginnen mit einer großen Zahl über Amazon oder über die Größe des Markts für Preissoftware. Die frühere Fassung dieses Artikels auch: die Zahl der Preisänderungen bei Amazon pro Tag, eine Marktprognose bis 2033, eine Umsatzzahl von McKinsey. Für keine davon habe ich eine Primärquelle erreicht, also sind sie gestrichen, und nichts unten hängt an ihnen.

Worauf es ankommt, ist etwas anderes. Bei dynamischer Preisgestaltung geht es nicht darum, Preise wild zu verändern. Die Europäische Kommission beschreibt sie in ihren Leitlinien zur Richtlinie über unlautere Geschäftspraktiken als Preise, die sich in erheblichem Umfang und sehr rasch nach der jeweiligen Nachfrage ändern, und hält fest, dass Händler ihre Preise frei bestimmen können, solange sie Verbraucher angemessen über den Gesamtpreis informieren.1 Gut gemacht, ruht sie auf einer einzigen Zahl, der Preiselastizität, und auf den Regeln, die einen Algorithmus davon abhalten, etwas technisch Optimales und kaufmännisch Unsinniges zu tun.

Das ist ein Leitfaden für die Praxis. Ich gehe durch, wie du die Elastizität aus deinen eigenen Bestellungen schätzt, wie du daraus einen Preis machst, wie die Preismaschine in Solidus und Rails ihn mit Leitplanken anwendet, und welche deutschen Regeln zu Preisangaben und Wettbewerb diese Maschine einhalten muss.

Durch den ganzen Artikel begleitet uns ein erfundenes Unternehmen. Rheinpfanne ist ein Onlineshop für Kochgeschirr in Köln unter rheinpfanne.example. Seine zwei Produkte in diesem Artikel sind eine gusseiserne Pfanne mit 26 cm, die im Einkauf samt Lieferung 20 € kostet, und eine Pfannenbürste der eigenen Marke, die 2 € kostet. Niemand davon ist echt, und jede Zahl unten ist erfunden, damit jeder Schritt sichtbar wird.

Die Preiselastizität ist die Zahl, auf die es ankommt

Die Preiselastizität der Nachfrage misst, wie stark sich die Nachfrage bewegt, wenn sich der Preis bewegt:

η=%ΔQ%ΔP\eta = \frac{\%\Delta Q}{\%\Delta P}

Dabei ist η\eta (Eta) die Preiselastizität, %ΔQ\%\Delta Q die prozentuale Änderung der verkauften Stückzahl und %ΔP\%\Delta P die prozentuale Änderung des Preises. Sie ist fast immer negativ, weil ein höherer Preis weniger Stück verkauft. Grundlagen: Elastizität

Hat ein Produkt eine Elastizität von minus 1,5, kostet eine Preiserhöhung um 10 % ungefähr 15 % der Stückzahl. Bei minus 0,3 kostet dieselbe Erhöhung ungefähr 3 %, und die Nachfrage merkt es kaum. Liegt der Betrag über eins, heißt die Nachfrage elastisch: Der Umsatz sinkt, wenn du den Preis erhöhst. Zwischen null und minus eins ist sie unelastisch: Der Umsatz steigt.

Durchgerechnetes Beispiel: zwei Produkte, eine Erhöhung um 10 %

Die Pfanne verkauft sich 200 Mal im Monat zu 36 €, und das Beispiel nimmt eine Elastizität von minus 2,5 an. Eine Erhöhung um 10 % auf 39,60 € behält auf einer Kurve mit konstanter Elastizität 1,10−2,5=0,791{,}10^{-2{,}5} = 0{,}79 der Stückzahl, also verkaufen sich etwa 158. Der Umsatz fällt von 200×36=7.200200 \times 36 = 7.200 € auf 158×39,60=6.257158 \times 39{,}60 = 6.257 €.

Die Bürste verkauft sich 300 Mal im Monat zu 6 €, mit einer angenommenen Elastizität von minus 0,6. Eine Erhöhung um 10 % auf 6,60 € behält 1,10−0,6=0,9441{,}10^{-0{,}6} = 0{,}944 der Stückzahl, also verkaufen sich etwa 283. Der Umsatz steigt von 300×6=1.800300 \times 6 = 1.800 € auf 283×6,60=1.868283 \times 6{,}60 = 1.868 €.

Dieselbe Erhöhung, gegensätzliche Ergebnisse. Deshalb ist eine einzige Preisregel für den ganzen Shop falsch.

Die Elastizität aus deinen eigenen Bestellungen schätzen

Du kannst die Elastizität aus deinen eigenen Verkäufen schätzen, solange ein Produkt ein paar Preisänderungen und genug Bestellungen hatte. Das Standardwerkzeug ist eine Regression mit Logarithmen auf beiden Seiten:

ln⁡Q=a+ηln⁡P+ε\ln Q = a + \eta \ln P + \varepsilon

Dabei ist ln⁡Q\ln Q der natürliche Logarithmus der wöchentlich verkauften Stückzahl, ln⁡P\ln P der Logarithmus des Preises in dieser Woche, aa eine Konstante, η\eta die Elastizität, die in dieser Form einfach die Steigung ist, und ε\varepsilon der Teil der Nachfrage, den das Modell nicht erklärt. Grundlagen: Regression und Grundlagen: Logarithmen

import json
import numpy as np
import pandas as pd
import statsmodels.api as sm

def estimate_elasticity(orders_csv, min_weeks=30, min_prices=3):
    """Export aus Solidus: product_id, price, quantity, order_date."""
    df = pd.read_csv(orders_csv, parse_dates=["order_date"])
    df["week"] = df["order_date"].dt.to_period("W").dt.start_time
    weekly = (df.groupby(["product_id", "week"])
                .agg(price=("price", "mean"), units=("quantity", "sum"))
                .reset_index())

    results = {}
    for product_id, g in weekly.groupby("product_id"):
        g = g[g["units"] > 0]
        if len(g) < min_weeks or g["price"].round(2).nunique() < min_prices:
            continue
        X = sm.add_constant(np.log(g["price"]))
        fit = sm.OLS(np.log(g["units"]), X).fit()
        results[str(product_id)] = {
            "elasticity": round(fit.params.iloc[1], 2),
            "std_error": round(fit.bse.iloc[1], 2),
            "weeks": len(g),
            "price_range": [round(g["price"].min(), 2), round(g["price"].max(), 2)],
        }
    return results

if __name__ == "__main__":
    print(json.dumps(estimate_elasticity("solidus_orders_export.csv"), indent=2))

Drei Änderungen gegenüber der früheren Fassung. Sie hat Wochen nur aus der Kalenderwoche gebildet, also flossen Woche 10 aus zwei verschiedenen Jahren zu einer zusammen. Sie hat vor dem Logarithmieren zu jeder Stückzahl 1 addiert, und das verbiegt die Steigung bei Produkten, die sich langsam verkaufen. Und sie hat keinen Standardfehler ausgegeben, dabei ist er die Zahl, die dir sagt, wie weit du der Elastizität trauen kannst.

Eine Warnung zählt mehr als jede Zeile Code. Preise in einem Shop ändern sich selten zufällig. Senkst du die Preise im Weihnachtsgeschäft, sieht die Regression niedrige Preise und hohe Verkäufe zusammen und schreibt dem Preis zu, was die Saison getan hat. Kontrollier Saison und Aktionen, sonst stimmt die Elastizität nicht. Darum geht es ausführlich in Nachfrage und Preiselastizität mit Regression modellieren, und das lohnt sich zu lesen, bevor du einer Zahl aus diesem Skript traust.

Ein Beispiel: Der Export für die Pfanne umfasst 52 Wochen mit vier verschiedenen Preisen zwischen 32 € und 40 €. Das Skript liefert eine Elastizität von minus 2,5 mit einem Standardfehler von 0,4. Eine grobe 95 % Spanne ist −2,5±1,96×0,4-2{,}5 \pm 1{,}96 \times 0{,}4, also minus 3,3 bis minus 1,7. Sicher elastisch, aber die genaue Zahl ist unscharf, und der nächste Abschnitt zeigt, warum das zählt.

Der Preis, der sich rechnet, folgt aus dem Deckungsbeitrag

Die Elastizität allein sagt dir den Preis noch nicht. Den Umsatz zu maximieren ist das falsche Ziel, weil der Umsatz übersieht, was jedes Stück kostet. Maximier den Deckungsbeitrag, also Preis minus Kosten eines weiteren Stücks, mal Stückzahl, und bei konstanter Elastizität ergibt sich eine saubere Regel, die Aufschlagsregel aus jedem Kurs in Volkswirtschaft:2

P∗=MC1+1/ηP^{*} = \frac{MC}{1 + 1/\eta}

Dabei ist P∗P^{*} der Preis, der den Deckungsbeitrag maximiert, MCMC die Grenzkosten, also die Kosten, ein weiteres Stück zu verkaufen, und η\eta die Elastizität, die unter minus eins liegen muss, damit die Regel eine Antwort gibt. Es ist dieselbe Regel wie in Nachfrage und Preiselastizität modellieren und, mit positiver Elastizität geschrieben, in Optimierung unter Unsicherheit für Budget und Preise. Grundlagen: Grenzwert und Durchschnitt

Durchgerechnetes Beispiel: die Aufschlagsregel bei der Pfanne

Schritt eins: 1/η=1/(−2,5)=−0,41 / \eta = 1 / (-2{,}5) = -0{,}4.

Schritt zwei: 1+(−0,4)=0,61 + (-0{,}4) = 0{,}6.

Schritt drei: P∗=20/0,6=33,33P^{*} = 20 / 0{,}6 = 33{,}33 €.

Die Pfanne kostet 36 €, etwas über der Antwort der Regel. Setz jetzt die Enden der Spanne aus dem letzten Abschnitt ein. Bei minus 3,3 liefert die Regel 20/(1−1/3,3)=20/0,697=28,7020 / (1 - 1/3{,}3) = 20 / 0{,}697 = 28{,}70 €. Bei minus 1,7 liefert sie 20/(1−1/1,7)=20/0,412=48,5720 / (1 - 1/1{,}7) = 20 / 0{,}412 = 48{,}57 €. Eine unscharfe Elastizität heißt ein unscharfer Preis, und deshalb bewegt die Maschine unten Preise in kleinen, protokollierten Schritten und misst, was passiert, statt zur Antwort der Formel zu springen.

Für die Bürste, bei minus 0,6, hat die Regel keine Antwort: Die Nachfrage ist unelastisch, also bringt jede Erhöhung im getesteten Bereich mehr Deckungsbeitrag. Das heißt nicht "für immer erhöhen". Es heißt, dass die Daten nichts über Preise oberhalb des Bereichs sagen, den du ausprobiert hast, und der nächste Schritt ein kleiner, vorsichtiger Test darüber ist.

Ein Nachfragemodell, kein Preismodell

Wenn die Elastizität verstanden ist, kannst du weitere Signale ergänzen: Lagerbestand, Saison, deinen Aktionskalender, die öffentlichen Preise der Konkurrenz. Die frühere Fassung dieses Artikels hat XGBoost darauf trainiert, den "Umsatz pro Stück" vorherzusagen, und die Vorhersage den optimalen Preis genannt. Das war falsch. Umsatz pro Stück ist einfach der Preis, der verlangt wurde, also hat das Modell gelernt, frühere Preise zu wiederholen, nicht einen besseren zu finden.

Die richtige Form ist ein Nachfragemodell. Sag die Stückzahl aus dem Preis und den anderen Signalen voraus, probier dann Kandidatenpreise aus und nimm den mit dem höchsten Deckungsbeitrag:

import numpy as np
import pandas as pd
from xgboost import XGBRegressor

FEATURES = ["price", "competitor_min_price", "days_of_stock", "week_of_year",
            "is_promotion", "category_demand_index"]

def fit_demand_model(history):
    model = XGBRegressor(n_estimators=300, max_depth=4, learning_rate=0.05,
                         monotone_constraints="(-1,0,0,0,0,0)")  # höherer Preis, nie mehr Stück
    model.fit(history[FEATURES], history["units"])
    return model

def best_price(model, today, unit_cost, current_price, step=0.05):
    candidates = np.round(current_price * np.arange(1 - step, 1 + step + 0.001, 0.01), 2)
    rows = [{**today, "price": p} for p in candidates]
    units = model.predict(pd.DataFrame(rows)[FEATURES])
    contribution = (candidates - unit_cost) * units
    return float(candidates[int(np.argmax(contribution))])

Die monotone Bedingung sagt den Bäumen, dass ein höherer Preis nie mehr Stück verkaufen kann, und das hält sie davon ab, aus verrauschten Wochen Unsinn zu lernen. Und die Kandidaten bleiben innerhalb von 5 % um den heutigen Preis, weil das Modell nur Preise kennt, die nah an denen liegen, die es gesehen hat. Teste es wie eine Prognose, an Wochen, die es nie gesehen hat, so wie ich es in KI gestützter Bedarfsprognose beschreibe.

Ein Beispiel: Für die Pfanne zu 36 € sagt das Modell 196 Stück bei 34,20 € und 172 bei 37,80 € voraus. Der Deckungsbeitrag bei 34,20 € ist (34,20−20)×196=2.783(34{,}20 - 20) \times 196 = 2.783 €. Bei 37,80 € ist er (37,80−20)×172=3.062(37{,}80 - 20) \times 172 = 3.062 €. Bei 36 € sagt das Modell 184 Stück voraus, also (36−20)×184=2.944(36 - 20) \times 184 = 2.944 €. Der beste Kandidat in diesem Fenster ist 37,80 €, eine Erhöhung, obwohl die Aufschlagsregel nach unten zeigte. Den Unterschied machen die anderen Signale: In diesem Beispiel ist der Bestand knapp, und ein Mitbewerber ist ausverkauft. Was davon richtig ist, entscheidet ein Test, kein Streit.

Die Leitplanken sind der wichtigste Teil

Das Modell wird manchmal etwas vorschlagen, das technisch optimal und kaufmännisch unsinnig ist. Stell dir vor, die Pfanne wird in einer Kochsendung erwähnt, und die Nachfrage verdreifacht sich über Nacht. Die reine Optimierung sagt: Preis um 40 % erhöhen. Bei dieser Nachfrage stimmt die Rechnung. Deine Kundinnen würden einen Screenshot machen, und der Ruf des Shops würde wochenlang dafür bezahlen.

Die Maschine schlägt deshalb vor, prüft und begrenzt dann. Die Prüfung kommt zuerst: Ein großer Vorschlag geht an einen Menschen, bevor eine Begrenzung verstecken kann, wie groß er war, dieselbe Reihenfolge wie in KI gestützter Marketingautomatisierung.

module DynamicPricing
  class Engine
    GUARDRAILS = {
      min_gross_margin: 0.25,  # Preis mindestens Kosten / (1 - 0.25)
      max_step: 0.05,          # höchstens 5 % pro Änderung
      review_above: 0.08,      # ein Vorschlag über 8 % geht an einen Menschen
      cooldown_hours: 24,      # höchstens eine Änderung am Tag
      max_above_rrp: 0.0       # nie über der unverbindlichen Preisempfehlung
    }.freeze

    def reprice(variant, proposed)
      current = variant.price.to_d
      change = ((proposed - current) / current).abs

      return PriceReviewQueue.create!(variant: variant, current: current, proposed: proposed) if change > GUARDRAILS[:review_above]
      return :cooldown if PriceChangeLog.where(variant: variant).where("created_at > ?", GUARDRAILS[:cooldown_hours].hours.ago).exists?

      price = constrain(variant, current, proposed)
      return :unchanged if price == current

      Spree::Variant.transaction do
        variant.update!(price: price) # Solidus hält den Preis im Standardpreis der Variante
        PriceChangeLog.create!(variant: variant, old_price: current, proposed: proposed, new_price: price)
      end
    end

    private

    def constrain(variant, current, proposed)
      step = current * GUARDRAILS[:max_step]
      price = proposed.clamp(current - step, current + step)
      floor = variant.cost_price / (1 - GUARDRAILS[:min_gross_margin])
      price = [price, floor].max
      rrp = variant.recommended_retail_price # ein eigenes Feld des Shops
      price = [price, rrp * (1 + GUARDRAILS[:max_above_rrp])].min if rrp
      price.round(2)
    end
  end
end

Eine Korrektur gegenüber der früheren Fassung. Sie hat cost * 1.10 eine "Mindestmarge von 10 %" genannt. Das ist ein Aufschlag von 10 %, und das ist eine Marge von 9,1 %. Die Untergrenze für die Marge ist Kosten geteilt durch eins minus Marge:

Pmin⁡=c1−mP_{\min} = \frac{c}{1 - m}

Dabei ist Pmin⁡P_{\min} der niedrigste Preis, den die Maschine setzen darf, cc die Kosten pro Stück und mm die Mindestbruttomarge als Anteil am Preis. Grundlagen: Prozente

Durchgerechnetes Beispiel: drei Vorschläge für die Pfanne

Die Pfanne kostet 20 €, also liegt die Untergrenze bei einer Marge von 25 % bei 20/(1−0,25)=26,6720 / (1 - 0{,}25) = 26{,}67 €. Ein Aufschlag von 25 % hätte nur 20×1,25=2520 \times 1{,}25 = 25 € ergeben, und bei 25 € ist die Marge 5/25=20 %5 / 25 = 20\,\%, nicht 25 %.

Montag: Das Modell schlägt 37,80 € statt 36 € vor. Die Änderung ist 1,80/36=5 %1{,}80 / 36 = 5\,\%, unter der Prüfgrenze von 8 %. Der Schritt von 5 % erlaubt bis zu 36×1,05=37,8036 \times 1{,}05 = 37{,}80 €, die Untergrenze liegt weit darunter, und die UVP liegt bei 40 €. Der Preis geht auf 37,80 €.

Dienstag: Das Modell schlägt 38,50 € vor. Die letzte Änderung liegt weniger als 24 Stunden zurück, also passiert nichts.

Nach der Kochsendung: Das Modell schlägt 52,90 € statt 37,80 € vor, eine Änderung von 15,10/37,80=40 %15{,}10 / 37{,}80 = 40\,\%. Über 8 %, also geht der Vorschlag unverändert in die Prüfliste, und der Inhaber von Rheinpfanne entscheidet. Nach den deutschen Regeln unten lautet die Antwort wahrscheinlich: Preis halten und mehr Ware bestellen.

Preisangaben und Lauterkeitsrecht bestimmen, was die Maschine darf

Das Verbraucherrecht verbietet dynamische Preise nicht. Die Leitlinien der Kommission sagen das deutlich, und sie nennen zugleich, wo es kippt: Erhöht ein Händler den Preis während des Kaufvorgangs, besonders nachdem der Verbraucher das Produkt in den Warenkorb gelegt hat oder zur Zahlung übergeht, ohne ihm eine angemessene Frist für den Abschluss zu lassen, kann das gegen die berufliche Sorgfalt verstoßen oder eine aggressive Praxis sein. Und Kosten erst im Lauf des Kaufs hinzuzufügen, die von Anfang an im Preis hätten stehen müssen, nennen sie Drip Pricing, eine irreführende Praxis.1 In Deutschland setzen das UWG und die Preisangabenverordnung (PAngV) diese Regeln um, dieselben, die ich für Versandkosten in klarem und konsistentem Messaging erkläre. Für eine Preismaschine zählen drei Teile.

Irreführend handelt, wer unwahre oder sonst zur Täuschung geeignete Angaben macht, unter anderem über den Preis und die Art, wie er berechnet wird.3 Als Vorenthalten einer wesentlichen Information gilt auch, wenn sie unklar, unverständlich oder zweideutig bereitgestellt wird oder nicht rechtzeitig kommt.4 Und wer ein Produkt mit Merkmalen und Preis so anbietet, dass man kaufen kann, muss den Gesamtpreis nennen und gegebenenfalls die zusätzlichen Kosten für Fracht, Lieferung und Zustellung, oder die Tatsache, dass sie anfallen können.5 Die PAngV verlangt den Gesamtpreis bei jedem Angebot und jeder Werbung mit Preisen gegenüber Verbrauchern,6 und im Onlineshop den Hinweis, dass die Umsatzsteuer enthalten ist, und die Höhe der Versandkosten, soweit sie sich vernünftigerweise im Voraus berechnen lässt.7 Durchgesetzt wird das UWG vor allem durch Abmahnungen von Mitbewerbern und Verbänden,8 und wer Gesamtpreis oder Versandkosten nicht, nicht richtig oder nicht vollständig angibt, begeht nach der PAngV eine Ordnungswidrigkeit.9

Zwei Regeln treffen eine Preismaschine besonders. Auf der Liste der Praktiken, die immer unzulässig sind, steht die unwahre Angabe, ein Angebot sei nur für einen sehr begrenzten Zeitraum verfügbar, um eine sofortige Entscheidung zu erzwingen.10 Und wer eine Preisermäßigung bekannt gibt, muss den niedrigsten Gesamtpreis der letzten 30 Tage vor der Ermäßigung angeben.11 Eine Maschine, die Preise rauf und runter schiebt und dann einen Streichpreis zeigt, muss also wissen, was der niedrigste Preis der letzten 30 Tage war.

Für die Maschine wird daraus eine kurze Liste von Regeln:

  • Den Warenkorb einfrieren. Ein Preis, der beim Angebot zum Kauf gezeigt wird, gilt bis zum Ende des Checkouts. Die Maschine ändert nie den Preis einer Variante, die in einem Warenkorb liegt, der gerade bezahlt wird.
  • Überall der Gesamtpreis. Jeder Preis, den die Maschine schreibt, erscheint als Gesamtpreis mit dem Hinweis auf die Umsatzsteuer und den Versandkosten mit Betrag, auf der Seite und im Feed.
  • Keine falsche Eile. Kein "Preis steigt in 10 Minuten", außer es stimmt, und kein Countdown neben einem Preis, der sich gar nicht gleich ändert.
  • Streichpreise nur mit dem 30 Tage Tiefstpreis. Die Maschine schreibt jeden Preis mit Datum ins Protokoll, damit der niedrigste Gesamtpreis der letzten 30 Tage jederzeit bekannt ist.
  • Sag, dass sich Preise bewegen. Eine schlichte Zeile auf der Produktseite oder auf der Seite zu Versand und Preisen, dass sich Preise mit Nachfrage und Bestand ändern.

Ein Beispiel: Eine Kundin legt die Pfanne um 21 Uhr zu 37,80 € in den Warenkorb. Um 21:05 Uhr läuft die Maschine und würde sie auf 39,69 € anheben. Weil die Pfanne in einem Warenkorb liegt, der den Checkout erreicht hat, wartet die Änderung. Die Kundin zahlt 37,80 € plus die Versandkosten, die sie auf der Produktseite gesehen hat. Der neue Preis gilt für die nächste Kundin, und die Änderung wird mit den Signalen protokolliert, die sie ausgelöst haben.

Kartellrecht: auf den Markt reagieren, nie mit ihm absprechen

Preise an der Konkurrenz auszurichten ist normal. Allein auf Preise zu reagieren, die deine Mitbewerber veröffentlichen, ist genau der Wettbewerb, den das Recht will. Die Grenze sind Absprachen und geteilte vertrauliche Daten.

Die britische Wettbewerbsbehörde CMA hält in ihrer Anleitung für Onlinehändler fest, dass Repricing Software gesunden Wettbewerb fördern kann und dass es rechtswidrig ist, mit ihr eine Preisabsprache umzusetzen. 2016 verhängte sie gegen einen Onlinehändler eine Geldbuße von über 160.000 Pfund, weil er mit einem Mitbewerber vereinbart hatte, einander auf der britischen Amazon Seite nicht zu unterbieten, umgesetzt mit automatischer Repricing Software.12 Das Bundeskartellamt und die französische Autorité de la concurrence beschreiben in ihrer gemeinsamen Studie zu Algorithmen zwei feinere Wege: einen Dritten, etwa einen Softwareanbieter, der Wettbewerbern denselben oder einen abgestimmten Algorithmus bereitstellt, oft Hub and Spoke genannt, und Algorithmen, die ganz ohne Kommunikation lernen, Preise hoch zu halten.13 In einem simulierten Markt haben KI Preisagenten auf Basis von Q Learning genau das getan.14 In Deutschland verbietet § 1 des Gesetzes gegen Wettbewerbsbeschränkungen (GWB) Vereinbarungen und abgestimmte Verhaltensweisen, die eine Verhinderung, Einschränkung oder Verfälschung des Wettbewerbs bezwecken oder bewirken,15 und die Geldbuße kann für ein Unternehmen bis zu 10 % des Gesamtumsatzes im Geschäftsjahr vor der Entscheidung erreichen.16

Die Maschine liest deshalb nur die eigenen Daten des Shops und die öffentlichen Preise der Konkurrenz. Kaufst du einen Repricing Dienst ein, frag, ob seine Empfehlungen nicht öffentliche Daten anderer Kunden nutzen. Die Spieltheorie dahinter und wo die Grenze für eine Plattform liegt, die konkurrierende Shops bedient, steht in wenn deine KI gegen sich selbst kämpft.

Diese öffentlichen Preise einzusammeln ist eine eigene Frage. Die frühere Fassung hat kommerzielle Scraping Dienste "für den Umgang mit Bot Schutz" empfohlen. Das habe ich gestrichen. Sperrt eine Website den automatischen Zugriff, respektier das. Lies und befolge ihre robots.txt, deren Standard RFC 9309 ist,17 lies ihre Nutzungsbedingungen, gib deinen Bot ehrlich mit einer Kontaktadresse zu erkennen und nimm lieber einen Feed eines Preisvergleichs oder die Preisliste eines Lieferanten, wo es sie gibt. Ob das Sammeln der Preise eines bestimmten Mitbewerbers zulässig ist, ist eine Frage für eine Anwältin, nicht für ein Skript.

Ein Beispiel: Ein Mitbewerber bietet dieselbe Pfanne für 34,99 € an. Die Maschine von Rheinpfanne sieht das auf der öffentlichen Produktseite des Mitbewerbers, einmal am Tag von einem ehrlichen Bot abgerufen, der sich an die robots.txt hält. Sie gibt den Wert als competitor_min_price in das Nachfragemodell, und das schlägt eine kleine Senkung vor. Das ist Wettbewerb. Eine Mail zwischen den beiden Inhabern, in der sie vereinbaren, dass keiner unter 35 € geht, wäre ein Kartell, egal welche Software es umsetzt.

Persönliche Preise sind etwas anderes, und riskanter

Alles oben bepreist nach Produkt, Bestand, Saison und Markt. Wer nach der Person bepreist, aus ihren personenbezogenen Daten, holt das Datenschutzrecht dazu. Nach Artikel 22 der Datenschutz Grundverordnung (DSGVO) hat ein Mensch das Recht, keiner Entscheidung unterworfen zu werden, die ausschließlich auf automatisierter Verarbeitung beruht und ihm gegenüber rechtliche Wirkung entfaltet oder ihn in ähnlicher Weise erheblich beeinträchtigt. Wo eine solche Entscheidung ausnahmsweise erlaubt ist, braucht es Schutzmaßnahmen, mindestens das Recht auf das Eingreifen einer Person, auf Darlegung des eigenen Standpunkts und auf Anfechtung der Entscheidung.18 Ob ein persönlicher Preis in diesem Sinn erheblich ist, hängt vom Fall ab. Unabhängig davon musst du im Onlineshop vor dem Kauf darauf hinweisen, wenn der Preis auf der Grundlage einer automatisierten Entscheidungsfindung personalisiert wurde.19 Nach Produkt statt nach Person zu bepreisen umgeht beide Fragen, und deshalb bepreist die Maschine oben nach Produkt.

So fängst du an

  1. Exportier deine Bestellungen. Produkt, Preis zum Zeitpunkt des Verkaufs, Menge, Datum. Das ist das Minimum für die Elastizität.
  2. Lass das Skript zur Elastizität laufen, mit Saison und Aktionen kontrolliert. Das allein zeigt dir, wo eine Preiserhöhung nicht wehtut, auch wenn du nie etwas automatisierst.
  3. Fang mit Regeln an, nicht mit einem Modell. "Sinkt die Reichweite des Bestands unter 14 Tage und lässt der Verkauf nach, senke um 3 %" ist nachvollziehbar und leicht zu prüfen.
  4. Nimm das Nachfragemodell dazu, wenn dich die Regeln einengen, getestet an Wochen, die es nie gesehen hat.
  5. Lass die Leitplanken an und friere Preise in Warenkörben ein, die gerade bezahlt werden.
  6. Protokollier alles. Jeden Vorschlag, jedes Signal, jede Leitplanke, die gegriffen hat, jeden Preis mit Datum. Das sind deine Trainingsdaten für das nächste Modell, dein Nachweis für den Tiefstpreis der letzten 30 Tage und deine Antwort, wenn jemand fragt, warum sich ein Preis geändert hat.

Lass die Maschine ein oder zwei Mal am Tag laufen. Dynamische Preise heißen nicht hektische Preise.

Quellen

Footnotes

  1. Europäische Kommission, Leitlinien zur Auslegung und Anwendung der Richtlinie 2005/29/EG über unlautere Geschäftspraktiken (2021/C 526/01), Abschnitt 4.2.8 Preisbildung, Amtsblatt der Europäischen Union C 526, 29. Dezember 2021, EUR Lex, https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:52021XC1229%2805%29

  2. Robert S. Pindyck, Lecture Notes on Pricing, 15.013 Industrial Economics for Strategic Decisions, MIT Sloan School of Management, August 2010, https://www.mit.edu/~rpindyck/Courses/Pricing_10.pdf

  3. Bundesministerium der Justiz, Gesetz gegen den unlauteren Wettbewerb (UWG), § 5 Irreführende geschäftliche Handlungen, Absätze 1 und 2 Nr. 2, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/uwg_2004/__5.html

  4. Bundesministerium der Justiz, Gesetz gegen den unlauteren Wettbewerb (UWG), § 5a Irreführung durch Unterlassen, Absätze 1 und 2, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/uwg_2004/__5a.html

  5. Bundesministerium der Justiz, Gesetz gegen den unlauteren Wettbewerb (UWG), § 5b Wesentliche Informationen, Absatz 1 Nr. 3, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/uwg_2004/__5b.html

  6. Bundesministerium der Justiz, Preisangabenverordnung (PAngV), § 3 Pflicht zur Angabe des Gesamtpreises, Absätze 1 und 3, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/pangv_2022/__3.html

  7. Bundesministerium der Justiz, Preisangabenverordnung (PAngV), § 6 Preisangaben bei Fernabsatzverträgen, Absätze 1 und 2, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/pangv_2022/__6.html

  8. Bundesministerium der Justiz, Gesetz gegen den unlauteren Wettbewerb (UWG), § 13 Abmahnung, Unterlassungsverpflichtung, Haftung, Absätze 1 und 3, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/uwg_2004/__13.html

  9. Bundesministerium der Justiz, Preisangabenverordnung (PAngV), § 20 Ordnungswidrigkeiten, Nr. 1, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/pangv_2022/__20.html

  10. Bundesministerium der Justiz, Gesetz gegen den unlauteren Wettbewerb (UWG), Anhang (zu § 3 Absatz 3), Nr. 7, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/uwg_2004/anhang.html

  11. Bundesministerium der Justiz, Preisangabenverordnung (PAngV), § 11 Zusätzliche Preisangabenpflicht bei Preisermäßigungen für Waren, Absatz 1, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/pangv_2022/__11.html

  12. Competition and Markets Authority, Price fixing: guidance for online sellers, GOV.UK, 7. November 2016, https://www.gov.uk/government/publications/price-fixing-guidance-for-online-sellers/price-fixing-guidance-for-online-sellers

  13. Bundeskartellamt und Autorité de la concurrence, Algorithms and Competition, November 2019, https://www.bundeskartellamt.de/SharedDocs/Publikation/EN/Berichte/Algorithms_and_Competition_Working-Paper.pdf?__blob=publicationFile&v=5

  14. Emilio Calvano, Giacomo Calzolari, Vincenzo Denicolò und Sergio Pastorello, Artificial Intelligence, Algorithmic Pricing, and Collusion, American Economic Review, Band 110, Heft 10, Seiten 3267 bis 3297, 2020, https://www.aeaweb.org/articles?id=10.1257/aer.20190623

  15. Bundesministerium der Justiz, Gesetz gegen Wettbewerbsbeschränkungen (GWB), § 1 Verbot wettbewerbsbeschränkender Vereinbarungen, Gesetze im Internet, abgerufen im November 2026, https://www.gesetze-im-internet.de/gwb/__1.html

  16. Bundesministerium der Justiz, Gesetz gegen Wettbewerbsbeschränkungen (GWB), § 81c Höhe der Geldbuße, Absatz 2, Gesetze im Internet, abgerufen im November 2026, https://www.gesetze-im-internet.de/gwb/__81c.html

  17. Martijn Koster, Gary Illyes, Henner Zeller und Lizzi Sassman, RFC 9309: Robots Exclusion Protocol, IETF, September 2022, https://www.rfc-editor.org/rfc/rfc9309.html

  18. Europäisches Parlament und Rat, Verordnung (EU) 2016/679 (Datenschutz Grundverordnung), Artikel 22 Absätze 1 bis 3, Automatisierte Entscheidungen im Einzelfall einschließlich Profiling, EUR Lex, 2016, https://eur-lex.europa.eu/legal-content/DE/TXT/HTML/?uri=CELEX:32016R0679

  19. Bundesministerium der Justiz, Einführungsgesetz zum Bürgerlichen Gesetzbuche (EGBGB), Artikel 246a § 1 Informationspflichten, Absatz 1 Nr. 6, Gesetze im Internet, 2026, https://www.gesetze-im-internet.de/bgbeg/art_246a__1.html

Wenn du wissen willst, welche deiner Produkte eine Preiserhöhung vertragen und welche nicht, bring ein Jahr Bestellungen in den kostenlosen Workshop mit, und wir rechnen die Elastizitäten gemeinsam durch. Die ersten zwei Wochen Arbeit gehen auf meine Rechnung.