Zum Hauptinhalt springen
engineeringbacktestingalgorithmen
Warum Backtests lügen: Indikatorbasierte Ausstiege zu simulieren ist schwerer, als es aussieht

Warum Backtests lügen: Indikatorbasierte Ausstiege zu simulieren ist schwerer, als es aussieht

Ein Backtest liefert eine Zahl, und Zahlen sind überzeugend. Genau das ist das ganze Problem. Wenn eine Simulation auf subtile Weise falsch ist, stürzt sie nicht ab und warnt Sie nicht. Sie reicht Ihnen ein etwas besseres Ergebnis als die Realität und lässt Sie danach handeln.

Einen festen Take Profit zu simulieren ist einfach: Der Preis hat Ihr Niveau entweder erreicht oder nicht. Ein indikatorbasierter Einstieg oder Ausstieg ist der schwierige Teil, denn jetzt muss die Simulation eine viel kniffligere Frage beantworten: Was wusste der Bot in diesem Moment tatsächlich?

Hier sind drei Wege, auf denen wir diese Frage falsch beantwortet haben, und was uns jeder davon gelehrt hat.

Das Wichtigste

  • Der klassische Fehler ist, einen Indikator eines höheren Zeitrahmens zu lesen, bevor dessen Kerze geschlossen hat. Ein 4-Stunden-RSI, der beim Kerzeneröffnung abgefragt wird, trägt vier Stunden Kursbewegung in sich, die noch gar nicht stattgefunden hatte.
  • Die Lösung ist Arithmetik, keine Heuristik: Ein Wert aus dem Zeitrahmen T ist erst dann legitim nutzbar, wenn T verstrichen ist. Verschieben Sie die Abfrage also um (Trigger-Zeitrahmen minus Simulations-Zeitrahmen) zurück.
  • Live-Engines haben dasselbe Problem in einem anderen Gewand. Ein Indikator, der auf einer sich noch bildenden Kerze berechnet wird, zeichnet neu (Repainting), das heißt eine Bedingung kann auf derselben Kerze wahr und dann wieder unwahr sein.
  • Ein Simulator, der still überspringt, was er nicht nachspielen kann, ist großzügiger als der Live-Bot, und die Lücke begünstigt immer den Backtest.
  • Der ehrliche Schritt ist, die Lücke im Ergebnis sichtbar zu machen, statt sie zu verstecken. Ein Backtest, der Ihnen sagt, wo er lügt, ist mehr wert als einer, der sauber aussieht.

1. Der Look-Ahead-Fehler, der sich in Multi-Zeitrahmen-Strategien versteckt

Dieser ist am leichtesten zu schreiben und am schwersten zu bemerken.

Nehmen wir an, die Simulation schreitet Kerze für Kerze im 5-Minuten-Takt voran, und die Strategie hat eine Bedingung auf einem 4-Stunden-RSI. Bei jedem 5-Minuten-Schritt braucht die Engine einen Wert für diesen RSI. Die naive Umsetzung schlägt die 4-Stunden-Kerze nach, die den aktuellen Moment enthält, und liest ihren Wert.

Das ist eine Zeitmaschine.

Eine 4-Stunden-Kerze, die um 12:00 eröffnet wurde, hat keinen endgültigen RSI-Wert, bis sie um 16:00 schließt. Wenn die Simulation bei 12:05 steht und diese Kerze liest, liest sie eine Zahl, die aus vier Stunden Kursbewegung berechnet wurde, die aus Sicht von 12:05 noch nicht stattgefunden hat. Die Strategie scheint genau vor Bewegungen einzusteigen, die sie nicht hätte kommen sehen können, und die Equity-Kurve sieht hervorragend aus.

Die Lösung ist keine Heuristik. Sie ergibt sich von selbst, sobald man aufschreibt, wann ein Wert tatsächlich bekannt sein kann. Eine Kerze, die zum Zeitpunkt t auf einem Zeitrahmen von triggerMs eröffnet, ist erst zum Zeitpunkt t + triggerMs bekannt. Die Simulation trifft ihre Entscheidung beim Schließen der aktuellen Kerze, candleOpenTime + simulationMs. Der Wert ist also genau dann legitim nutzbar, wenn:

t + triggerMs <= candleOpenTime + simulationMs
t             <= candleOpenTime - (triggerMs - simulationMs)

Das bedeutet, die korrekte Abfrage ist die, die Sie bereits hatten, ausgeführt gegen einen um triggerMs - simulationMs zurückverschobenen Zeitstempel.

Zwei Eigenschaften dieses Ausdrucks sind es wert, hervorgehoben zu werden, denn genau sie machen es sicher, ihn überall anzuwenden:

  • Bedingungen im selben Zeitrahmen erhalten eine Verschiebung von null. Ein 5-Minuten-Indikator, der beim Schließen einer 5-Minuten-Kerze gelesen wird, ist sein eigener schlussbasierter Wert, der schon immer korrekt war. Nur echte höhere Zeitrahmen verschieben sich.
  • Die Verschiebung wird abgeleitet, nicht konfiguriert. Sie stammt aus der Kerze selbst und nicht aus einer Einstellung, an die sich jemand erinnern muss.

Die allgemeine Lektion würde ich auf jede Simulation anwenden, nicht nur auf das Trading: Wenn Sie unsicher sind, ob Sie einen Look-Ahead-Bias haben, schreiben Sie den Zeitstempel auf, zu dem jede Eingabe bekannt wird, und vergleichen Sie ihn mit dem Zeitstempel, zu dem Sie sie verwenden. Bias ist das, was übrig bleibt, wenn Sie diesen Schritt überspringen und sich stattdessen auf Intuition verlassen, und Intuition ist darin schlecht, weil „die aktuelle 4-Stunden-Kerze" zu lesen völlig vernünftig klingt.

2. Derselbe Fehler im Live-Code trägt ein anderes Gewand

Man könnte meinen, dieses Problem gehöre zum Backtesting. Tut es nicht. Die Live-Engine steht ihm ebenfalls gegenüber, und dort heißt es Repainting.

In jedem Moment ist die jüngste Kerze noch in Bildung. Ein Indikator, der über ein Fenster berechnet wird, das auf dieser Kerze endet, ändert sich, während sich die Kerze ändert. Eine Bedingung kann um 12:01 wahr sein, um 12:03 falsch und um 12:04 wieder wahr, alles innerhalb derselben Kerze, ohne dass der Markt etwas Ungewöhnliches tut. Wenn der Bot auf die erste davon reagiert, hat er auf einen Wert reagiert, der nicht mehr existiert.

Unsere Voreinstellung ist, die sich noch bildende Kerze zu verwerfen und nur auf bestätigten Kerzen zu rechnen, sodass ein Signal nie neu zeichnet. Genau das bringt auch Live und Backtest zur Übereinstimmung, denn die Simulation arbeitet konstruktionsbedingt mit geschlossenen Kerzen.

Aber „nie neu zeichnen" ist nicht umsonst zu haben. Auf Bestätigung zu warten bedeutet, später zu handeln, und für manche Strategien kostet diese Verspätung mehr als das Rauschen, das sie vermeidet. Deshalb wird dies als Wahl pro Bedingung angeboten: auf der bestätigten Kerze auswerten oder live auswerten und akzeptieren, dass der Wert sich unter Ihnen bewegen kann.

Den Designgedanken würde ich beibehalten: Wenn ein Zielkonflikt wirklich ein Zielkonflikt ist, vergraben Sie ihn nicht in einer Voreinstellung und tun so, als sei er eine Tatsache. Machen Sie die sichere Option zur Voreinstellung und lassen Sie den Nutzer die andere bewusst wählen. Was Sie nicht tun dürfen: die beiden Modi zwischen Live und Backtest voneinander abweichen lassen, denn dann vergleicht der Nutzer zwei verschiedene Bots.

3. Was der Simulator nicht nachspielen kann, ist gefährlicher als das, was er falsch macht

Die ersten beiden sind Korrektheitsfehler. Dieser ist ein Designproblem, und es ist das, das meiner Meinung nach am meisten unterschätzt wird.

Manche Bedingungen können nicht nachgespielt werden. Ein externes Webhook-Signal ist der klarste Fall: Es gibt keinen historischen Datensatz darüber, was ein externes System an einem Tag im vergangenen März um 14:20 gesendet hätte. Die Simulation kann es schlicht nicht wissen.

Was sollte sie also tun? Die verlockende Antwort ist, diese Bedingung zu überspringen und den Rest auszuwerten. Das ist es, was das Ergebnis vollständig aussehen lässt. Es ist auch die Stelle, an der der Schaden entsteht, und der Mechanismus ist es wert, ausbuchstabiert zu werden:

Wenn eine Strategie A UND B erfordert und B nicht simuliert werden kann, dann lässt das Überspringen von B A allein entscheiden. Der simulierte Bot steigt bei Bedingungen ein, bei denen der Live-Bot draußen geblieben wäre. Die Simulation ist systematisch großzügiger als die echte Sache.

Beachten Sie die Richtung. Der Fehler ist nicht zufällig. Eine Bedingung unter UND-Logik fallen zu lassen kann immer nur mehr Trades erzeugen als die Realität, nie weniger, sodass der Bias jedes einzelne Mal auf ein besser aussehendes Ergebnis zeigt. Diese Asymmetrie ist es, die ihn gefährlich macht: Zufälliges Rauschen mittelt sich über viele Durchläufe heraus, ein einseitiger Bias nicht.

Wir haben uns entschieden, die Simulation weiterlaufen zu lassen, statt sie zu verweigern, denn ein Teilergebnis mit einer bekannten Lücke ist immer noch nützlich. Aber dann muss die Lücke in der Ausgabe sichtbar sein, nicht in einem Log vergraben. Das Ergebnis trägt eine ausdrückliche Warnung, in der eigenen Sprache des Nutzers, mit einem Namen, der sagt, was passiert ist: „Einige Einstiegsbedingungen wurden nicht simuliert." Es gibt eine passende für Ausstiege.

Diese Entscheidung ist der Teil dieses Systems, den ich am entschiedensten verteidigen würde. Ein Simulator, der nicht laut scheitern kann, wird leise scheitern, und leises Scheitern bei etwas, das Zahlen produziert, ist der schlimmste Fehlermodus, den es gibt, denn Menschen handeln nach Zahlen und nicht nach Stacktraces.

Was Sie daraus mitnehmen sollten

Wenn Sie irgendetwas bauen, das Historie nachspielt, um eine Zahl zu erzeugen:

  • Jede Eingabe braucht einen „bekannt ab"-Zeitstempel. Wenn Sie ihn nicht angeben können, können Sie einen Look-Ahead-Bias nicht ausschließen.
  • Prüfen Sie die Richtung Ihrer Fehler, nicht nur deren Größe. Ein Fehler, der immer in dieselbe Richtung zeigt, summiert sich auf; einer, der zufällig zeigt, mittelt sich heraus. Einseitiger Optimismus ist die Signatur eines Look-Ahead-Bias.
  • Lassen Sie Simulator und Live-System ihre Entscheidungslogik teilen, wo immer Sie können. Zwei Umsetzungen von „ist diese Bedingung wahr" werden auseinanderdriften, und Sie erfahren es von einem Nutzer statt von einem Test.
  • Melden Sie, was Sie nicht tun konnten. Die Glaubwürdigkeit eines Ergebnisses kommt aus seinen genannten Grenzen, nicht aus seiner Sauberkeit.

Die unbequeme Zusammenfassung ist, dass ein Backtest eine Behauptung über ein Kontrafaktisches ist, und Kontrafaktisches lässt sich leicht still falsch machen. Das Ingenieursziel ist kein Simulator, der nie lügt. Es ist einer, der Ihnen sagt, wo er lügt.


Weiterführende Lektüre

Bereit, Ihren Handel zu automatisieren?

Starten Sie Ihre Reise mit Freya Finance und lassen Sie KI-gestützte Bots für Sie arbeiten.

Kostenlos starten
Dieser Inhalt dient nur zu Bildungszwecken und stellt keine Finanzberatung dar. Der Handel mit Kryptowährungen birgt erhebliche Risiken. Investieren Sie nie mehr, als Sie sich leisten können zu verlieren.