Repository navigation
Paper engine: replay fills in execution order per symbol, never into a short (#355 follow-up) — Claude review pending - #362
Conversation
…a short (#355 follow-up) Implements the #355 reviewers' constraints for execution-time replay: - Stream fills carry Alpaca's top-level trade_updates timestamp, and FILL recovery and startup persistence carry transaction_time. All are stored losslessly as executions.execution_time_ns (integer ns). The ledger schema_version goes to 2, so a previous release refuses to open a migrated ledger, and the execution insert names its columns. - Per-symbol replay: timed fills are substituted only into timed journal slots; adoptions, untimed fills and uncovered provisional bookings keep their positions. If the time-ordered pass would go short, that symbol falls back to observation order, which is always feasible. It raises only if the observation pass itself fails. - Targeted trigger: replay only when the triggering order was provisional or has an execution earlier than an already-booked timed fill of the same symbol. Money is kept per symbol, so one symbol's journal cannot block another symbol's fills. - Open-time re-derivation when the accounting rule marker is not current, so _begin_trial sees corrected loss before any new fill. Tests (all fail on 460034e where new): - A/B/C gives cash 70, realized -15, loss 25, 1 @ 115 under five deliveries. - B2 is identical in both arrival orders (flat, cash 75, realized -25, loss 35), with no rollback and no freeze. - An unreplayable journal cannot block another symbol's fill. - Stream, recovery and startup paths carry exact ns. - The schema migration is refused by the previous open check. - Old markers re-derive at open. Reverting the three production files gives 18 failures and 1 error. Paper suites: 425 OK; source-hashes and trading gates: 33 OK; validate.py passed. Implemented by GPT-6 (gpt-6-astra, effort max) test-first, verified by the coordinator. The independent Claude review is pending (Claude session limit until 22:10Z). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Independent Claude review at This was a headless [major]
[minor] [minor] [minor] Verified OK:
Checks run by the reviewer:
Coordinator reproduction on a merge preview (main + head):
Next: one bounded repair round. GPT-6 writes the fix test-first in its own worktree, preferring "don't backfill legacy rows", which adds no replay cost. A headless Claude re-check of the repair diff and a GPT-6 read-only re-check follow. Merge waits for a main-free window in the token lane's queue. |
…nreplayable symbols at open (#362 review repair) Repairs the independent Claude review of 20a3296 (1 major, 3 minor). - major: _record_execution no longer backfills execution_time_ns onto an existing untimed row. Untimed executions keep NULL times and their journal positions, and a timestamped re-delivery is a no-op, so booked money and the loss-cap halt no longer depend on delivery order (startup, recovery, stream and migrated ledgers). - minor: a marker subtest where the rule key is present but out of date. - minor: re-derivation at open isolates an unreplayable symbol (both replay SafetyErrors), keeps its booked money and records the symbol and reason. Corrupted legacy symbols without a money checkpoint still fail atomically. - minor: the RFC 3339 parser accepts a lowercase z and truncates fractions beyond nanoseconds. 8 new regression methods fail on 20a3296 and pass here. The paper suites (435) and the hash and gate tests (45) pass with TMPDIR outside the worktree, and validate.py passes. Implemented test-first by GPT-6 (gpt-6-astra, max); the coordinator verified it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qwXNtFyUhY5x2dtrm7tG5
|
Repair round pushed as GPT-6 (
Checks
Before this merges, whenever the paper lane reopens:
|
What
This is the #355 follow-up: book fills in execution order without creating a new failure mode. An earlier attempt, reverted in #355, was refuted because it could reorder a fill into a short position and freeze the transport, and because its replay was global. This design takes the #355 reviewers' constraints as requirements.
Exact execution time. Each path records the execution's own time:
trade_updatestimestamp(notorder.updated_at);transaction_time.The time is stored losslessly as
executions.execution_time_ns. The ledgerschema_versionmoves to 2, so the previous release refuses to open a migrated ledger, and the execution insert names its columns.Never reorder into a short. Replay is per symbol. Timed fills are substituted only into timed journal slots; adoptions, untimed fills and uncovered provisional bookings keep their positions. If the time-ordered pass would go short, that symbol falls back to observation order, which is always feasible. It raises only if the observation pass itself fails, which would mean corruption.
Targeted trigger. Replay runs only when the triggering order was provisional, or when it has an execution earlier than an already-booked timed fill of the same symbol. Money is tracked per symbol, so for a ledger written by this PR one symbol's journal cannot block another symbol's fills (see Limitations for ledgers written by main).
Re-derive at open. When the accounting-rule marker isn't current, the ledger re-derives at open, so
_begin_trialsees the corrected loss before any new fill.Tests (each fails on 460034e where the behaviour is new)
validate.pypassed.gpt-6-astra, effort max, workspace-write) implemented this test-first, and the coordinator verified it.Review status (2026-09-29). The repair (
20a32965..1afd7522) got its cross-family Claude re-check from an Opus 5.5evidence-reviewer(effort max): no code findings, and the body corrections below applied. An Opus 5.5stack-verifierthen ran a local merge preview of maincf3fb72eplus1afd7522. The adaptive-paper suites, the trading gates andvalidate.pyall passed (verdict approve).Limitations
transaction_timethat arrives later, at startup or recovery, is ignored for such a row, so the fill keeps journal order. Its realized P&L, gross loss and loss-cap checks can therefore differ from execution-order accounting (repair fixture: loss 20 against 25; a cap of 25 does not halt).SafetyErrors (accounting_replay_quantity_mismatchcan also raise); other malformed-journal errors still fail the open.test_later_replay_cannot_reprice_untimed_historymeans untimed rows keep their row, slot and NULL time. A late timed fill can still move an earlier timed fill past an untimed one (20a3296's slot substitution), so the untimed fill's basis can change.SOTA sources
timestamp,execution_id): https://docs.alpaca.markets/docs/websocket-streamingtransaction_time): https://docs.alpaca.markets/docs/account-activities985ef30: per-fill closing P&L inCommon/Securities/SecurityPortfolioModel.cs; losses separate inCommon/Statistics/TradeStatistics.cs🤖 Generated with Claude Code