Skip to content

Phase C: NautilusTrader vs hftbacktest cross-check with an independent oracle; engines agree after fixing our driver's use-after-free - #245

Merged
seathatflowsinourveins merged 4 commits into
mainfrom
claude/sim-engine-crosscheck-20260925
Sep 25, 2026
Merged

seathatflowsinourveins merged 4 commits into
mainfrom
claude/sim-engine-crosscheck-20260925

Conversation

@seathatflowsinourveins

Copy link
Copy Markdown
Owner

What

This is Phase C of the sim-lane engine work. The same bounded order stream is replayed through NautilusTrader 2.0.0rc5, the sim-capacity engine from #235, and hftbacktest 2.4.4, the independent engine installed in #232. Each order's outcome is then checked against a new engine-independent oracle built from the retained SIP quotes.

  • Sample: SPY and NVDA, 2026-09-24 14:05–14:13Z.
  • Order stream: 1,439 orders at 3 per second, marketable limits with a 1¢ collar, quantity 5.
  • Configurations: 70 ms and 250 ms latency, and both hftbacktest exchange models.

Result

The engines agree, and the sim-capacity overturn condition does not fire.

Config Fill rate Median time-to-fill Oracle agreement
Nautilus 70 ms, as in sim-capacity 97.64% 111.23 ms 99.65%
Nautilus 70 ms, exact latency 97.71% 70.00 ms 99.72%
hftbacktest 70 ms (either exchange model) 97.98% 70.00 ms 100.0%
Nautilus 250 ms, as in sim-capacity 94.23% 290.60 ms 99.72%
Nautilus 250 ms, exact latency 94.44% 250.00 ms 99.93%
hftbacktest 250 ms (either exchange model) 94.51% 250.00 ms 100.0%
  • No fill beats the displayed quote. There are zero better-than-touch fills across all 8 runs, and the report build now fails if one appears.
  • The overturn rule is quoted verbatim from sim-capacity/experiment.json and evaluated on these corrected numbers. Fill rates differ by 0.30–0.36% (the threshold is 20%), and the median time-to-fill ratio is 1.16–1.59× (the threshold is 2×).

The first attempt was wrong, and it is kept on record

The first attempt (d5cc0c5) reported a divergence of more than 20%, all of it in NVDA (hftbacktest 53% against Nautilus 97%). It explained the gap as a model difference (F1) and a suspected upstream bug (F2).

An independent adversarial review (Opus 5.5) built its own oracle and refuted both. The real cause was a use-after-free in our own driver: NVDA's event array was freed while hftbacktest's Rust reader still held a non-owning pointer to it.

  • 72,134 of 74,835 NVDA events changed in memory.
  • Reruns on the same input gave different results, and one run hung.

The repair round (08a684e):

  • keeps every event array referenced until the engine closes;
  • submits orders at exact times, and counts filled quantity as qty − leaves_qty;
  • adds oracle.py, a hard better-than-touch check and a determinism command;
  • withdraws F1 and F2.

The superseded receipt is kept byte-identical as receipts/20260925-crosscheck-superseded.json. The sim-capacity record marks the old observation failed, then appends the correction.

A real, separate Nautilus finding

This is a timing-realism issue. It does not make fills optimistic.

  • Behaviour: Nautilus releases a latency-delayed order at the next quote on its own instrument, or at the next timer.
  • Size of the slack: a median of 28 ms on SPY and 53 ms on NVDA beyond the configured latency.
  • Fix, tested: one time alert per order at submit + latency. Every order then resolves at exactly the configured latency.
  • Effect on prices: they stayed within 1¢ of the touch at submit + latency.
  • Follow-up: apply the fix to sim-capacity's own order generator in a separate PR, then re-run its receipts.

Review

  1. Substance review (Opus 5.5). Refuted the first conclusion, as above.
  2. Repair (the builder).
  3. Re-check of the fix (Opus 5.5). Ready.
    • It matched all 8 configurations with its own oracle.
    • It confirmed the fix from hftbacktest source.
    • The isolation test failed 5 of 5 with the bug patched back in.
    • Its minor corrections (which tests really guard, attribution of the residual gap, the locked-quote note, and three citation nits) are recorded in the README's "Independent re-check" section. The test now pins hftbacktest's agreement at its measured 100%.

Codex cross-family review is unavailable until 2026-09-30, so it was skipped.

Checks

  • This branch's tests: tests.test_sim_engine_crosscheck and tests.test_sim_capacity pass on system Python: 113 tests, of which 32 are engine-gated skips. The builder ran the cross-check tests on both pinned runtimes, and the re-check reran them with hftbacktest installed.
  • Validators: validate.py, evidence_manifest.py --check and validate_convergence.py --all-recorded pass.
  • Secret scan: gitleaks is clean on every commit.
  • Main merged in at 4b594d4, keeping both sides' appended limitations in sim-capacity/experiment.json.

Evidence class and limits

  • Class: a simulation cross-check, infrastructure evidence only. It is not broker throughput and not strategy performance (NS-06).
  • Coverage: one 8-minute, two-symbol sample, 2 latency points and 2 exchange models.
  • Private files: per-order outcomes and the sample stay host-local. Only aggregates and hashes are committed.
  • Open residuals: the report attaches oracle agreement but does not enforce it at build time, and metrics.py skips fills that have no quote in force.

🤖 Generated with Claude Code

Scout and others added 3 commits September 25, 2026 04:16
Add blueprints/us-equities/sim-engine-crosscheck/: replays one shared,
deterministic order stream (reusing sim-capacity's own schedule.py) over a
bounded SPY+NVDA Alpaca SIP sample through both NautilusTrader 2.0.0rc5
(sim-capacity's engine) and hftbacktest 2.4.4 (PartialFillExchange and
NoPartialFillExchange), at 70ms and 250ms latency, and compares fill rate,
time-to-fill, touch share and cost via engine-agnostic metrics.

Overall fill rate diverges by >20% at both latencies (sim-capacity's own
overturn condition), but the divergence is concentrated almost entirely in
NVDA (SPY agrees within -0.3%) and is attributed to a documented
model-semantics difference: NautilusTrader releases a deferred order at its
own instrument's next quote or any due timer, while hftbacktest evaluates
against the book at exactly submit+latency -- on a sparser, gappier name
this keeps Nautilus orders marketable far more often. Median time-to-fill
stays within 2x at both latencies. Conclusion: diverges for a documented
model-semantics reason, not a matching bug.

A separate, smaller effect is also documented: hftbacktest's own
PartialFillExchange and NoPartialFillExchange disagree on NVDA at 70ms only
(vanishing at 250ms), traced via direct instrumentation and pinned-source
reading to PartialFillExchange's depth momentarily reporting the "no
quote" sentinel where NoPartialFillExchange's does not, on byte-identical
fed data. Flagged as a suspected hftbacktest-internal inconsistency, not
filed externally per policy, with proposed next steps.

Registers the new files in manifests/evidence.json and appends a dated
cross-check result to sim-capacity/experiment.json (observations, commands,
frozen_inputs.tasks and limitations all extended additively; no existing
entries rewritten), validated with scripts/validate_convergence.py.

Tests (tests/test_sim_engine_crosscheck.py) are hermetic and synthetic:
order_stream.py and metrics.py have no engine dependency and are checked
unconditionally; NautilusTrader- and hftbacktest-driver smoke tests skip
cleanly without the corresponding runtime and were verified passing on both
pinned runtimes plus system Python. All six repository validators pass;
gitleaks (dir and git-history mode) finds no leaks. The hftbacktest venv
this task rebuilt was removed afterward per the existing blueprint's own
cleanup convention.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…racle

An independent adversarial review refuted the first attempt's conclusion
(commit d5cc0c5) outright. Root cause, confirmed by this task's own
independent reproduction: a use-after-free in run_hftbacktest.py -- a
per-symbol numpy feed array lost its only Python reference when the loop
variable holding it was reassigned to the next symbol, while hftbacktest's
Rust side kept a raw, non-owning pointer into that freed memory for the
rest of the run. Running the unmodified old driver twice gave different
outcome hashes (non-determinism); NVDA-alone vs NVDA-within-a-two-symbol-run
also gave different hashes (cross-symbol contamination). Both checks are
now hash-identical with the fix applied.

Withdraws both prior findings: F1 (an ~82% NVDA fill-rate divergence
"explained" by Nautilus latency-release timing) and F2 (a PartialFillExchange
vs NoPartialFillExchange NVDA-only disagreement) were both artifacts of the
corruption, not real engine behavior. Corrected: NautilusTrader and
hftbacktest agree within 0.30-0.36% fill rate and 1.16-1.59x median
time-to-fill (1.0x against a new NautilusTrader exact-latency variant) --
sim-capacity's 20%/2x overturn condition is not triggered, and the
NautilusTrader destination is not overturned.

Fixes: keep every per-symbol feed array referenced through hbt.close();
submit orders at their exact scheduled time (elapse() to each order's own
ts_ns) instead of a 10ms polling grid; read filled quantity as
qty - leaves_qty, not exec_qty (which upstream holds only the last fill
call's own quantity). Adds oracle.py, an independent engine-free
last-NBBO-crossing predictor (>=99.58% agreement with every engine/symbol
in the corrected data) and a hard better-than-touch report check (zero
violations across all 8 runs; the first attempt never ran this against
hftbacktest's own output -- it would have caught the corruption directly).
Reports a separate, minor, real NautilusTrader timing finding (release at
the next quote-or-timer, not exactly at submit+latency), with a fix for
sim-capacity's own exerciser recommended as a separate follow-up PR, not
made here.

The first attempt's receipt is preserved unmodified at
receipts/20260925-crosscheck-superseded.json (not deleted); its sim-capacity
experiment.json observation is marked failed/superseded (not rewritten in
place) with a new, passing repair-round observation appended.

Tests: adds hermetic oracle.py and better-than-touch checks, receipt-level
oracle-agreement/violation assertions, and runtime-gated two-symbol-vs-
single-symbol isolation plus determinism-rerun checks -- both verified
failing against the unmodified pre-fix driver before this round's fix,
passing after. All 51 cross-check tests pass on system Python and both
pinned runtimes. All six repository validators pass; gitleaks (dir and
git-history mode) finds no leaks. The hftbacktest venv rebuilt for this
round was removed afterward.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eck; pin hftbacktest's oracle agreement

Merge of main at 4b594d4. sim-capacity/experiment.json keeps both
sides' appended limitations (this branch's two cross-check entries,
then main's #242 frozen-source re-pin note) and main's re-pinned
replay_compare.py digest. manifests/evidence.json is main's registry
plus this branch's 11 registrations, with every digest recomputed.

The independent re-check of 08a684e returned "ready": its own oracle
reproduced all 8 configurations exactly, and it confirmed the fix from
hftbacktest source. Its minor corrections are recorded in a README
section. The run's code and receipt bytes are left as produced, since
the receipt pins their hashes. The corrections: only the isolation test
is a reliable use-after-free guard; the Nautilus-to-oracle residual is
short-sale rejects plus timing; locked quotes changed no outcome; and
three citation nits.

The receipt test now pins hftbacktest's oracle agreement at its
measured 100% (Nautilus keeps the 99% floor).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

A full python3 -m unittest run failed with 18 errors
(AttributeError: 'metrics' has no attribute
'better_than_touch_violations'). adaptive-paper/metrics.py shares the
bare module name, and whichever suite imported it first won
sys.modules['metrics']. The test now loads
sim-engine-crosscheck/metrics.py via spec_from_file_location as
sim_engine_crosscheck_metrics. oracle and order_stream keep plain
imports, since their names are unique and OrderIntent must stay one
class. Both import orders now pass in one process.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit 7ea3a78 into main Sep 25, 2026
23 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/sim-engine-crosscheck-20260925 branch September 25, 2026 12:57
seathatflowsinourveins added a commit that referenced this pull request Sep 25, 2026
… engine merges and credential blocker (#248)

- Simulation lane: hftbacktest agrees with NautilusTrader within 0.36%
  once our driver's use-after-free is fixed (#245). The evidence
  reference points to the cross-check receipt.
- Paper lane: the engine P0 release (#221) and exact cancel identity
  (#242) merged today. The native-fault rerun and the 1x ladder attempt
  are blocked: at the open, the stored paper keys were rejected with
  HTTP 401 on the trading account and on both data feeds, so they need
  rotating through the credential terminal. No order was sent.

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant