"""Beweist, dass die bisect-Fassung des Pivot-Fensters BITGENAU dieselbe Menge liefert wie die frühere lineare Filterung. HINTERGRUND (2026-08-07): das Profil zeigte, dass **8,17 s von 10,07 s** Gesamtlaufzeit in der Eigenzeit der Backtest-Schleife steckten — nicht in `_build` (0,44 s). Ursache war [C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K] je Bar über die KOMPLETTE Pivot-Liste, also O(Bars × Pivots). Bei 80k Bars sind das hunderte Millionen Vergleiche — daher die 9–20-Minuten-Läufe. ⚠ Die Migrations-Regel des Projekts lautet: ein Backtest darf nur umgestellt werden, wenn er danach DIESELBEN Zahlen liefert — sonst ist es keine Refaktorierung, sondern eine unbemerkte Änderung der Messgrundlage. Weil ein Vorher/Nachher-Vergleich zweier LÄUFE hier nicht taugt (`copy_rates_from_pos` ankert am neuesten Bar, zwischenzeitlich eintreffende Live-Bars verschieben das Fenster — real am 06.08. ein Trade Unterschied), wird die Gleichheit hier DIREKT auf der Datenstruktur geprüft. """ import random from bisect import bisect_left, bisect_right from backtest_auto_signal_v3 import _PIV_K, _PIV_LOOK def _alt(PH, C, i): """Die frühere, lineare Fassung.""" return [C[j] for j in PH if i - _PIV_LOOK <= j <= i - _PIV_K] def _neu(PH, C, i): """Die bisect-Fassung, wie sie jetzt in `lauf()` steht.""" a = bisect_left(PH, i - _PIV_LOOK) b = bisect_right(PH, i - _PIV_K) return [C[j] for j in PH[a:b]] def test_fenster_ist_identisch(): """Über eine realistisch geformte Pivot-Liste und viele Bar-Positionen.""" rnd = random.Random(42) n = 4000 C = [70 + rnd.gauss(0, 2) for _ in range(n)] # Pivots wie im Backtest: aufsteigend, unregelmäßig verteilt PH = sorted(rnd.sample(range(_PIV_K, n - _PIV_K), 400)) for i in range(_PIV_LOOK, n, 7): assert _alt(PH, C, i) == _neu(PH, C, i), f"Abweichung bei i={i}" def test_randfaelle(): """Die Stellen, an denen eine Fensterlogik typischerweise bricht.""" C = list(range(1000)) PH = [10, 20, 30, 300, 305, 310, 700] for i in (0, _PIV_K, _PIV_LOOK, _PIV_LOOK + _PIV_K, 313, 999): assert _alt(PH, C, i) == _neu(PH, C, i), f"Abweichung bei i={i}" def test_leere_pivotliste(): C = list(range(100)) assert _alt([], C, 50) == _neu([], C, 50) == [] def test_grenzen_sind_inklusiv(): """`<=` auf BEIDEN Seiten — bisect_left/bisect_right bilden genau das ab. Ein `bisect_left` auf der oberen Grenze würde das Randelement verlieren.""" C = list(range(1000)) i = 500 PH = [i - _PIV_LOOK, i - _PIV_LOOK + 1, i - _PIV_K - 1, i - _PIV_K] assert _neu(PH, C, i) == [C[j] for j in PH] # alle vier drin assert _alt(PH, C, i) == _neu(PH, C, i)