Repository navigation
report sink: retire sealed closed-trade rows on sink routes when the module opts in - #591
Closed
luisleo526 wants to merge 1 commit into
Closed
luisleo526 wants to merge 1 commit into
luisleo526 wants to merge 1 commit into
Conversation
…module's export allows it With a report sink installed, a streamable policy and a strategy module whose existing export strategy_reads_trade_history() returns zero, closed-trade descriptors and text below the published base are released after the existing readers have consumed them; a read below the base refuses with the existing engine_invariant (empty arguments). Every other run keeps its complete history and the no-sink contract is unchanged. Report bytes, folds and hashes do not change. - The identity row compares the candidate with a frozen reference produced by the unchanged engine and committed under tests/data/trade_history_reference (generated by scripts/regenerate_trade_history_reference.py at the pinned base, SHA-256 manifest verified before use, 4.7 MB). The comparison normalizes only the engine string of the sink header and the trailer digest that covers it, so a release version bump needs no new data. - strategy_history_policy.hpp uses dladdr/dlopen/dlsym, so both archives link the dynamic loader library (CMAKE_DL_LIBS). - The new trade rows are slow rows and are added to the pinned slow set. - Documentation anchors into c_abi.cpp, scratch_file.hpp and CMakeLists.txt are re-pinned after the line shifts of this change. The memory page text that this change reworded or removed is named here for the documentation revert check: - A sink moves report rows to its path or fd stream while the ledger's external scratch still needs a writable, disk-backed backing store. - Older rows and all string payloads: 168 B plus text per closed trade - Install a report sink; the sink route is bounded - Adapter state (placement rows, settled bracket families, script containers) is separate and has its own rows below - Heap bounded for the report; adapter state as the rows below - A default run with no report sink needs a writable, disk-backed scratch directory even when it makes no trade. - The spilled trade rows are `O(T)` on disk with a small resident window (see below). - C or C++ host with no sink, and jailed Python without `--report-sink-fd` (the default route of the C API): equity curve - Install a report sink (above); a bounded default route is planned, with no release date - Same route (no sink): closed trades - `T`: 168 B plus text per trade - Scratch, with 64 descriptor rows resident and a read cache of up to 65,536 entries (312 B each, allocated as trades accumulate); the FULL report built - Install a report sink; no release date for the default route - Any report sink installed (native or Pine host, history streamed or not) - The heap is bounded for the equity curve (one held point and one 64 KiB batch) and for the ledger (64 descriptor rows and a 128-entry read cache). - The trailing block of rows that share the last exit time stays resident only when history is streamed (`K` lots closed at one time, `O(K)`). Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
| import json | ||
| from pathlib import Path | ||
| import re | ||
| import unittest |
| from pathlib import Path | ||
| import shutil | ||
| import tempfile | ||
| import unittest |
luisleo526
added a commit
that referenced
this pull request
Oct 28, 2026
…aling, UDT graphs, metrics replay and the CI admission fix (#616) This lands twelve engine pull requests as one change, verified and measured once on the combined tree: - #593 scratch: a fixed write-through read cache for the adapter's id tables - #594 live runner: a delta report stream by default, and report-path work that does not rescan closed trades - #597 live runner: typed pre-growth stop for the ledger budget and flat pending counts - #608 run_json: reserve the report channel, so strategy output on fd 1 cannot corrupt a report - #586 Pine UDT object graphs on the paged object Store - #578 native order: dormant working book, flat per-point cost for never-filled orders - #579 adapter: skip roster passes and empty-family scans that cannot change anything - #599 adapter: read projected pending orders through one cached duplicate index - #563 adapter: gate the reversal exit cancel on candidate owners instead of every historical id - #591 report sink: retire sealed closed-trade rows on sink routes when the module opts in - #596 metrics: stream the bar Sharpe and Sortino through an ordered, packed return replay - #615 ci: accept release.yml's matrix runner under its fetched path in PR admission Fixes found while testing the combined tree are in the final commits: the retiring ledger's cache sizing, a residency witness that bounds sink-run heap by the declared cache cap and checks zero growth at 100k, 200k and 1M trades, a test fix for a descriptor reuse race, and a standing-orders timing grid whose 20,000-order point is tracked in #621. The member pull requests are closed as landed here.
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #583 as one commit on main (same product code; the commit message names every replaced memory-page sentence).
What changes
Eligible report-sink runs release closed-trade descriptors and text after the existing readers have consumed the sealed prefix. Indexed reads into that released prefix refuse explicitly; runs that retain history continue to return the original rows.
Retirement requires all of: an installed path or borrowed-fd sink,
set_trade_history_streamable(true), and an owning strategy module whose existing generated exportstrategy_reads_trade_history()returns zero. The codegen already emits that export and pins it across every closed-trade read shape (codegen #213, #264), so no new symbol and no compiler change are needed. A missing symbol, a nonzero result, a failed lookup, an exception, or unproved module ownership retains full history. The lookup is module-local and image-checked (the symbol must resolve in the same image as the strategy object).The existing seal boundary stays authoritative. Trade records, identity strings, report/metric/money folds and hashes are consumed before release; a failed write advances no retirement base and publishes no trailer. Below-base
closed_trade,get_trade,previous_closed_trade, Pine field reads and C metadata reads on admitted retiring runs carry the existingengine_invariantwith empty arguments{}. The C metadata accessors keep their existing return sentinels.What does NOT change
No-sink runs retain their complete owning history, even when the module exports zero. Nonstreamable scripts and modules that export nonzero or nothing retain historical indexed reads. Logical indices, seal predicates, mutable exit-time groups, report bytes, numeric folds, broker/source hash recipes, installed layouts and the runtime failure-code and export sets are unchanged. Physical storage origins are excluded from logical hashes.
The byte-identity proof compares complete sink bytes, metrics, every per-bar source/broker hash and the final hashes with a frozen reference produced by the unchanged engine (see "How to verify"); the reference is committed under
tests/data/trade_history_reference/(15 files, 4,716,341 bytes in total) with a SHA-256 manifest, so the check runs in ordinary CI.Tests
test_report_sink_trade_retirementtest_report_sink_trade_historytest_report_sink_trade_legacytest_report_sink_trade_identitytest_report_sink_trade_referencetest_sink_history_consumerstest_report_sink_trade_residencyResult of the six rows above on the pushed history before the final rename commit: all passed on a disk-backed lab VM (retirement 204 s, history 191 s, legacy 189 s, identity 13.5 s, reference 0.17 s, consumers 0.8 s). The rows, the six verification legs and the parity sweep are re-run on the head of this PR.
Stated limits
How to verify
scripts/regenerate_trade_history_reference.py --base-tree <clean worktree of the commit in tests/data/trade_history_reference/BASE_COMMIT>builds the legacy executable only from that base tree in a fresh harness, writes the artifacts and the manifest, and refuses to publish more than 5,000,000 bytes. It never uses the changed library.ctest -R '^test_report_sink_trade_(identity|reference|retirement|history|legacy|residency)$|^test_sink_history_consumers$'on a disk-backed runner.python3 scripts/ci_preflight.py(documentation anchors, source inventory), clean full Release verification withscripts/ci_verify.py, and the corpus parity sweep; all report 0 differences for a change that must not alter any report byte.🤖 Generated with Claude Code