Apply syminfo.mincontract as the engine's lot grid in the Docker harness - #322
Merged
Merged
Conversation
The harness applied only mintick, pointvalue, timezone and session from the --syminfo file, so a percent-of-equity strategy traded sub-lot quantities that the instrument does not allow. A positive finite mincontract is now set as the engine's qty_step metadata (order quantities are floored to it) and as the mincontract metadata, before the other setters. Absent or null changes nothing; any other value, or a library without the metadata setter, fails the run with the structured error line, which shows the value's JSON text. An applied grid is recorded as applied_runtime.syminfo and so in the fingerprint. pineforge-release vendors docker/run_json.py from the engine tag at every release, so the change has to live here to reach a published image: this is the same file as pineforge-release#21, whose copy the next sync overwrites. docker/README.md, the docker/entrypoint.sh header comment and CHANGELOG.md document it; docker/run_json_syminfo_test.py covers it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Tests: restore the checks the port from pineforge-release dropped (SyminfoError is a ValueError; the grid sits in the digested token bytes in canonical form and the plain token has no syminfo) and pin the recorded grid to floats, so an integer mincontract is not recorded as an int. Docs: the error text shows the parsed value re-encoded as JSON, not the file's text; a file that is not JSON or not an object still ends in a traceback; a library without the metadata setter fails the run only when mincontract is set (CHANGELOG); the PINEFORGE_SYMINFO row leaves the "runtime args" table (it is applied through the syminfo setters and has its own section); the extra-keys object is named plainly. docker/run_json.py and docker/entrypoint.sh are unchanged. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| ], ids=["absent", "null", "wrapped-null"]) | ||
| def test_mincontract_absent_or_null_applies_no_grid(tmp_path, doc, setters): | ||
| lib = FakeLib(setters) | ||
| assert run_json.apply_syminfo(lib, ST, write(tmp_path, doc)) == {} |
Bring the PR up to date so the strict required-status rule applies to the current base. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 4, 2026
luisleo526
added a commit
that referenced
this pull request
Oct 4, 2026
docker/run_json.py --symbol-feeds (PINEFORGE_SYMBOL_FEEDS in the entrypoint)
reads a JSON index of other symbols' bars, keyed by the exact symbol string a
script passes to request.security and by timeframe, and installs each symbol's
catalog syminfo (tickerid as canonical, type, timezone, session, currency,
mintick) and feeds through strategy_set_symbol_facts and
strategy_set_symbol_feed, which engine and codegen have shipped since 1.0.0
but no release harness called. A feed is an OHLCV CSV like --ohlcv; each
bar's close is its open plus the timeframe unless a time_close column gives it.
Everything is checked before the first strategy state: timeframe spelling
(bare D/W/M/S folded, "4h" refused), one feed per timeframe, CSV columns and
numbers, increasing opens, closes at or before the next open, mintick, the
256-feed cap. A refusal, a library without the setters or a feed the engine
refuses is the structured {"engine","error"} line (harness exit 1, entrypoint
exit 4), like a rejected --syminfo. What was installed is recorded as
applied_runtime.symbol_feeds (facts, bar counts, first/last open, a value
hash), so such a run has its own fingerprint; without --symbol-feeds the
report is unchanged.
Stacked on harness/syminfo-mincontract (#322): it reuses that change's
structured-failure path in main().
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
luisleo526
added a commit
that referenced
this pull request
Oct 4, 2026
…s (--symbol-feeds) (#327) * Install other symbols' bars for request.security in the Docker harness docker/run_json.py --symbol-feeds (PINEFORGE_SYMBOL_FEEDS in the entrypoint) reads a JSON index of other symbols' bars, keyed by the exact symbol string a script passes to request.security and by timeframe, and installs each symbol's catalog syminfo (tickerid as canonical, type, timezone, session, currency, mintick) and feeds through strategy_set_symbol_facts and strategy_set_symbol_feed, which engine and codegen have shipped since 1.0.0 but no release harness called. A feed is an OHLCV CSV like --ohlcv; each bar's close is its open plus the timeframe unless a time_close column gives it. Everything is checked before the first strategy state: timeframe spelling (bare D/W/M/S folded, "4h" refused), one feed per timeframe, CSV columns and numbers, increasing opens, closes at or before the next open, mintick, the 256-feed cap. A refusal, a library without the setters or a feed the engine refuses is the structured {"engine","error"} line (harness exit 1, entrypoint exit 4), like a rejected --syminfo. What was installed is recorded as applied_runtime.symbol_feeds (facts, bar counts, first/last open, a value hash), so such a run has its own fingerprint; without --symbol-feeds the report is unchanged. Stacked on harness/syminfo-mincontract (#322): it reuses that change's structured-failure path in main(). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Address review of the symbol-feeds harness change - Every refusal is the structured line, never a traceback: a feed that is not UTF-8 (or a CSV error), a time outside +-(2^53-1) ms or a monthly close outside the calendar, a mintick integer beyond binary64, a symbol or fact string with a lone surrogate, an index nested past the recursion limit. - A header-only feed is installed without bars, as the engine documents it (its requests read na), instead of refused. - An empty time_close cell falls back to open plus timeframe; a UTF-8 BOM is read; error line numbers are the CSV's own (blank lines counted). - Install-time refusals carry the same "--symbol-feeds:" prefix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Document the symbol-feed limits and that no feed is aggregated The harness README gains a limits bullet (256 symbols and 256 feeds, the chart as its own input with the engine's exact error, historical runs only, the cost of a feed, request.security_lower_tf) and states that a feed must be at the timeframe it serves: the engine aggregates only the chart's own input and the C ABI has no call that aggregates bars, so a 1m feed serves neither a 240 nor a D request. The CHANGELOG entry says the same. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Correct the symbol-feeds README on syminfo reads and aggregation Review fixes. Inside the request syminfo.tickerid is always the key and syminfo.ticker the key after its last ":"; the catalog tickerid is set as the canonical fact, which no syminfo.* reads. The no-aggregation bullet now says why (the engine looks a feed up by exact symbol and timeframe and installs its bars as given) instead of claiming no C call aggregates. The missing-feed sentence covers an index that lacks the requested symbol or timeframe, and the refusal list is marked as examples ("timestamps that do not strictly increase"). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Place the symbol-feeds CHANGELOG entry after the confirmed-bar streams one After the rebase onto #325 the entry sat at the top of Unreleased; it now follows #325's entry, as the merge order asks. Wording unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
luisleo526
added a commit
that referenced
this pull request
Oct 4, 2026
* docs: release notes and version scoping for v1.1.0 v1.1.0 is a minor release: <pineforge/pineforge.h> declares six more functions, the checked settings calls that a strategy generated by pineforge-codegen 1.1.0 exports (#317). It pairs with pineforge-codegen 1.1.0. PF_ABI_VERSION stays 4 (pineforge.h only gains declarations; native_c_api.h and the native C++ host headers are unchanged) and the script ABI epoch stays engine_script_run_v19. - CHANGELOG.md: "## Unreleased" becomes "## 1.1.0 — 2026-10-04": a lead paragraph, one bullet per change a user can observe since v1.0.1 (#312-#324), each linking its PR, a Report keys subsection (docker/run_json.py v1.0.1 -> v1.1.0) and a Migration subsection. The three Unreleased bullets stay; the lot-grid bullet now gives both failure messages. - README.md: pineforge-codegen==1.1.0, the v1.1.0 tarballs, the pairing sentence, the stream known issue's scope, the benchmark refresh line, the release scoreboard sentence (releases[1.1.0] markers) and a v1.1.0 line in Releases. The public corpus sweep's "311 excellent + 1 declared anomaly" is scoped to the v1.0.1 library it was measured on. Facts rendered from the facts file that adds releases[1.1.0]; the active scoreboard markers move to baseline pineforge-parity-baseline-20261003-engine-dbd17b38. - docs/pages/install.md: the v1.1.0 tarballs and the hub tag engine1.1.0-codegen1.1.0. - docs/pages/public-contract.md: 1.1.0's release line, engine v1.1.0 with codegen 1.1.0 among the supported pairs, and the four C-boundary rows that were planned for 1.1.0 are not in it. - docs/pages/streaming.md: the known issue's scope includes v1.1.0. Lines that state what an earlier version shipped or did stay. Replaces text introduced by dc74e63 (#318): README.md's "Release 1.0.1 still grades ... until the next release" sentence, now 1.1.0's; by d093560 (#290): public-contract.md's "Four of its rows are planned for 1.1.0"; by 783bb39 (#322): CHANGELOG.md's "The engine library is unchanged."; by dbd17b3 (#320): CHANGELOG.md's "The engine is unchanged."; by b2a578c (#300): README.md's "this repository's sweep: 311 excellent + 1 declared anomaly"; by 271d687 (#311): README.md's "its releases 1.0.0 and 1.0.1 do". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: state the 1.1.0 hash folds and scope two runner sentences The 1.1.0 lead now says how state-hash values change while the epoch and the domain tags stay: a run whose fills an adapter change alters hashes to new values, and three changes fold more state, so a run can hash differently from 1.0.1 with byte-identical trades: #315 (an unbatched same-bar entry request and placement; the stream report's terminal re-mark leaves the hashed extremes; witness row Stream/0/1 re-pinned), #316 (an opening stop's next waypoint; Random44/0/0 and /1 re-pinned) and #319 (tick-volume state, every existing pin kept). In the runner tooling bullet, "No versioned engine PF_API export ... changes" and "Plugin-free ledger identity bytes remain unchanged" read as release-wide; they now speak for #320 itself and for a strategy without the checked settings calls. The Report keys intro points at the report schema page. The terminal-quote bullet names the chart feed that PINEFORGE_RUN_REPORT_CHART_QUOTE points at, and two runner bullet headings are re-wrapped. Replaces text introduced by a80485c (this branch's first commit): the lead's "A run whose fills one of the Pine adapter changes below alters hashes to new values, and [#316] adds one placement field to the adapter's hashed state, for which two witnesses with byte-identical trades were re-pinned.", the Report keys intro and the terminal-quote harness clause; and text introduced by dbd17b3 (#320): "No versioned engine PF_API export, native C++ surface, script ABI epoch or engine behavior changes." and "Plugin-free ledger identity bytes remain unchanged". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: #315's range-end change folds less, not more The 1.1.0 lead said three changes "fold more state"; #315's range-end change instead stops folding a stream report's terminal re-mark into the hashed extremes. The sentence now says the three changes change what the hashes fold, and names Stream/0/1 as a re-pinned witness row without claiming it is #315's only one. Replaces text introduced by 66be240 (this branch's second commit): "Three changes also fold more state" and "re-pinning one witness row (`Stream/0/1`, whose recorded-row digest alone moves)". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: fold #325 and #327 into 1.1.0, add State hashes, amend the hash rule #325 (confirmed-bar streams compute what the batch computes) and #327 (docker/run_json.py --symbol-feeds) merged before the release notes, so their Unreleased bullets join the 1.1.0 section, rewritten and linked; no Unreleased heading stays. - CHANGELOG.md: the lead names the stream fix and, in the pairing sentence, codegen 1.1.0's request discovery (transpile_full()["requests"], codegen #164). #325's bullet says what a stream user sees; #327's bullet and its Report keys entry (applied_runtime.symbol_feeds, the --symbol-feeds: failure messages). A new State hashes section lists the four recipe additions since v1.0.1 (#315, #316, #319, #325; none removed, renamed or re-encoded; domain tags unchanged). #315's range-end equity-extreme change moves to the Pine adapter parity bullet as a behaviour fix. The #319 bullet named the broker-state hash; the change is in the native continuation hash, which the broker-state hash folds. The known-issue bullet now says it is fixed in 1.1.0 (#325). Migration gains a line on stream results and hashes. - README.md: the known issue is fixed in 1.1.0 (#325); the v1.1.0 Releases line gains the stream fix, request discovery and the harness's multi-symbol request.security feeds. Fact markers rendered from the facts export that maps releases[1.1.0] to pineforge-parity-baseline-20261004-engine-7b596622 (7,951 excellent / 38 strong of 7,989); the active scoreboard moves to the same baseline, here and in docs/pages/contributing-llm.md. - docs/pages/streaming.md: the known issue affected v1.0.0 and v1.0.1 and is fixed in 1.1.0. - docs/pages/public-contract.md: the state-hash rule, amended by the owner's ruling for 1.1.0. It covers the recipe only: a minor release may add a hashed field (domain tags unchanged, the added field changing no trade or report, disclosed under State hashes); removing, renaming or re-encoding a hashed field is a new epoch and a major release; behaviour changes follow the normal release and parity rules. The first uses are #315 and #316. The semver rule is unchanged. Replaces text introduced by 7b59662 (#325): CHANGELOG.md's "Confirmed-bar streams compute what the batch computes:" bullet, and the "Fixed on main (#325); included in the next release." lines of README.md and streaming.md; by 4f2a475 (#327): CHANGELOG.md's "Harness symbol feeds:" bullet; by d093560 (#290): public-contract.md's "State-hash values are stable within the epoch." and "a new recipe is a new epoch and a major release"; by a80485c (this branch's first commit): the "Known issue, as in v1.0.0 and v1.0.1" bullet, streaming.md's "(v1.0.0, v1.0.1, v1.1.0)", the #319 bullet's "A tick stream's broker-state hash folds the new accumulator state only where it adds information, so the existing tick, bar and batch hash pins hold.", the Report keys failure-output bullet and the v1.1.0 Releases line; by 66be240 and 83d64dc: the lead's state-hash sentences ("Three changes also change what the hashes fold"); and, as git attributes them through this branch's merge of main, by e59c09f: README.md's v1.1.0 Releases line and streaming.md's "(v1.0.0, v1.0.1, v1.1.0)". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.
What
docker/run_json.py(the harnessdocker/entrypoint.shruns) now appliesmincontractfrom the--syminfofile (PINEFORGE_SYMINFOin the entrypoint) as the engine's lot grid. Until now it applied onlymintick,pointvalue,timezoneandsession, so a percent-of-equity strategy traded quantities below the instrument's lot size.mincontractis set through the existingstrategy_set_syminfo_metadataexport, as the keyqty_step(the engine floors order quantities to that grid) and asmincontract(what a script'ssyminfo.mincontractreads), before the other four setters.null: nothing is applied. The report and its fingerprint are byte-for-byte what they were, apart fromelapsed_seconds.0,-1,"0.001",true,NaN,Infinity, a list, an object), or a strategy library withoutstrategy_set_syminfo_metadata: the run fails before it starts with one{"engine":"pineforge","error":"syminfo.mincontract must be a positive finite number, got <value>"}line on stdout (harness exit 1, entrypoint exit 4). It never runs without the grid it was given.applied_runtime.syminfo({"qty_step": v, "mincontract": v}), so it is infingerprint.provenance.runtimeand a gridded run has its own digest.The engine library and the C ABI are unchanged.
docker/README.md, thedocker/entrypoint.shheader comment andCHANGELOG.mddocument it.Why here
pineforge-release vendors
docker/run_json.pyfrom the engine tag at every release (scripts/sync-harness.shand the sync step of itshandle-upstreamworkflow), which overwrites its own copy. The same change is open there as pineforge-4pass/pineforge-release#21 (head 857096f), and it only reaches a published image if the file also lands here.docker/run_json.pyon this branch is byte-identical to that PR's file.The case
One frozen case: BTCUSDT 4h, EMA 20/50 crossover, long only, 100 % of equity, 0.1 % commission, initial capital 10,000, 2,188 bars of Binance public spot klines, engine 1.0.1 and codegen 1.0.1 inside the release image.
mincontractnull or absentmincontract0.00001sha256:e4315ec2…sha256:b9e00cb9…Tests
docker/run_json_syminfo_test.py(pytest) covers the setter order and values, absent and null, every rejected value with its message, a library without the setter, the declared C signature,main()with the report and the fingerprint,--bench(every state gets the grid, none leaks), and the structured failure in the body run and in both bench loops. On main's harness 32 of its 37 tests fail; on this branch all 44 tests indocker/pass. Like the olderdocker/*_test.pyfiles, it is not registered with ctest or CI (it runs withpython3 -m pytest docker/); wiring those tests in is a separate change.docker/Dockerfile(engine 1.0.1 static library, codegen 1.0.1), once with main'srun_json.pyandentrypoint.shand once with this branch's. On the case, main returns 27 rows and ignoresmincontract. This branch returns 22 rows withmincontractand 27 rows without it or withnull, row by row equal to the recorded rows (1e-9).-1,0,"0.001"andtrueexit 4 with the error line above.mincontractare byte-identical between main and this branch, withelapsed_secondsset to 0, for six runs: no syminfo file; the case's mintick, pointvalue, timezone and session only; the full instrument record withmincontractnull; the same record without the key; and a second instrument (mintick 0.5, pointvalue 2) without the key and withnull.scripts/fingerprint_self_test.py,scripts/test_report_schema_keys.pyandtests/test_entrypoint_indir.shpass.🤖 Generated with Claude Code