feat: judge.review compares each paper trade with the simulator

For a paper day, every order is lined up with what the validated simulator does on
the same recorded ticks: the sim exit from the real fill (stop, 6% trail after +1R,
20-min time stop, sell at the bid 300 ms after the trigger) vs the paper exit,
plus the sim R of orders that did not fill. Trades are flagged when R differs by
more than 0.3R, the exit reason differs, or the entry slipped. The report goes to
data/judge/reviews/<date>.md.

On 2026-09-30: paper -8.25R vs sim -3.97R on the same six fills. The gap is two
trades where Alpaca's paper engine filled a triggered stop 6-60 s late (CMCT,
PWCM; PWCM's own exit was also blocked by the stop-id bug fixed in f3c8be1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3PXXn2tHBzdv6rYtGPz86
This commit is contained in:
momentum devandClaude Opus 5.5 committed 2026-10-01 07:11:54 -05:00
1 parent 358b859c59
commit 5315e99978
2 files changed
+191

No files matched your search

+31
View File
@@ -0,0 +1,31 @@
"""judge.review.sim_exit reproduces the simulator's exit rules on synthetic ticks."""
import pandas as pd
from judge.review import sim_exit
T0 = pd.Timestamp("2026-09-30 10:00:00")
def tape(prices, step_s=1):
t = [T0 + pd.Timedelta(seconds=step_s * (i + 1)) for i in range(len(prices))]
T = pd.DataFrame({"t": t, "p": prices, "s": 100})
Q = pd.DataFrame({"t": t, "bp": [p - 0.01 for p in prices], "bs": 100, "ap": [p + 0.01 for p in prices], "as": 100})
return T, Q
def test_stop_sells_at_the_bid_after_the_breach():
T, Q = tape([10.0, 9.9, 9.5, 9.4])
s = sim_exit(T, Q, T0, 10.0, 9.5)
assert s["reason"] == "stop" and round(s["exit"], 2) == 9.49 and round(s["r"], 2) == -1.02
def test_trail_after_one_r_gives_back_six_percent():
T, Q = tape([10.5, 11.0, 12.0, 11.2, 11.0]) # risk 0.5: +1R at 10.5; trail 6% under 12.0 = 11.28
s = sim_exit(T, Q, T0, 10.0, 9.5)
assert s["reason"] == "trail" and round(s["exit"], 2) == 11.19
def test_time_stop_after_twenty_minutes():
T, Q = tape([10.1] * 30, step_s=60)
s = sim_exit(T, Q, T0, 10.0, 9.5)
assert s["reason"] == "time"