Make the futures provider actually runnable: norgate extra, volume=, and the port verification - #8
Merged
Merged
Conversation
The provider cannot be exercised anywhere but the Windows box, and the one consumer that would have failed loudly on a wrong series — crowdmon, whose strict tier requirements were why the work order put it first — was deprecated with no consumers left. Nothing exercises propadj until the deferred npf pass. So the comparison against cotdata's existing store is the only evidence the port preserved the numbers, and it is available only until ADR-0007 §7.5 deletes the half being compared against. That makes it worth more than two commands and an eyeball. The script reads both stores' parquet directly, no network and no imports of either package, and exits non-zero so it can gate the promotion. Same shape as cotdata's validate_databento_vs_norgate.py, which is the precedent for a real-data harness kept out of CI with its logic unit tested. The tolerance is the part worth noting. That neighbouring harness compares two INDEPENDENT vendors, whose roll calendars and back-adjust anchors differ, so only shape can agree. This compares one vendor through two code paths, so the passthrough columns must be EXACTLY equal and a float away is a port bug. The exception is the volume reconstruction: both producers compute it identically but incrementally over their own store's history, so a fresh marketdata store and a months-old cotdata one legitimately differ there. Those columns are reported rather than failed on, with --strict-volume as the opt-in and a pointer at cotdata's --full. The harness itself is tested here, because a verifier that reports identical on frames that differ is worse than none — it would clear the port for deletion of the only thing it can ever be compared against. 19 tests: a single changed bar is caught and located, matching NaNs are agreement rather than difference, a reconstruction difference does not mask a price difference, MME/MFS are an expected skip rather than a failure, and a typo'd store path exits 2 instead of reading as "nothing differed". Verified end to end on seeded stores: one planted bad bar in 36,000 rows was found and dated, with the reconstruction drift correctly a note. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
`--domain futures` stopped at the import guard on the Windows producer because nothing installs norgatedata: the provider landed with a dependency no extra declares. cotdata has carried `norgate = ["norgatedata"]` all along and the port did not bring it across. The guard behaved correctly — it refused rather than half-producing — but its message sent the reader to --domain equities, which is right for a Mac or Linux box and exactly wrong for the machine that is supposed to produce futures. A missing package and a Windows-only vendor are two problems with two different fixes, and the message now separates them and names the install. Two tests, because the guard being right is what hid this. One asserts the extra exists and carries norgatedata, so a provider whose vendor package nothing installs cannot ship again. The other asserts the message names both the extra and the platform constraint, so it cannot quietly regress to advice that only suits the machines which were never going to run it. README gains the Windows producer install line for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
… meet The variable was documented as required, and config.py refuses by name when it is unset, but every example was `export ...` — bash, on a repo whose only producer machine runs Windows. cotdata carries a whole Windows setup guide with `set`, System Settings, .env and an `echo %VAR%` check; marketdata carried nothing. The larger gap is newer than the port. That box now runs BOTH producers, so it needs COTDATA_STORE and MARKETDATA_STORE set at the same time against different roots, and nothing said so. Until ADR-0007 moved bars here, COTDATA_STORE alone was the whole story. So: setx rather than set, with the reason spelled out, since a variable that lives only in the current prompt is the usual cause of a scheduled task failing where an interactive shell works. Then --check as the cheap confirmation, being manifest-only and offline. And an explicit warning against one shared root: sharing a parent folder is fine and makes the pair easy to sync, but each package keeps a manifest.json at its own root and does a read-modify-write on it, so one root means the two producers eventually drop each other's entries. Python, venv and Task Scheduler setup are identical to cotdata's and are pointed at rather than duplicated. Only the norgate extra and these two variables are marketdata-specific. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
…ence as agreement The first real run passed on every passthrough column across 49,892 rows and five symbols, and printed nothing at all about the reconstruction columns. That was predicted to print notes, so the silence was the finding. Two causes, both mine. The verifier reported reconstruction columns only when they DIFFERED, so "compared and identical" and "never compared" rendered as the same nothing. A PASS therefore could not be read as "everything was compared", which is the one thing this harness exists to establish before ADR-0007 §7.5 deletes the only store it can ever compare against. It now names every column family it skipped and why, and prints the ones that matched. And the reason there may have been nothing to compare: get_bars never grew the volume= parameter. The producer half was ported — _reconstruct_volume writes Volume_Reconstructed, FirstVolume, SecondVolume and Volume_Source — but the consumer switch that serves them was not, so cotdata's get_prices(volume="reconstructed") had no counterpart here. npf's ml/labels.py passes volume= through, so a repointed call would have raised TypeError. The docstring carries crowdmon's measurement rather than restating the names, because the names are a trap that points the opposite way to intuition: `reconstructed` sums exactly two expiries and is 0.52 of total volume in natural gas and 0.54 in crude, so `front` is what a whole-market denominator wants. A test pins that reconstructed < front so the docstring cannot drift from it. Equities refuse the parameter outright, since summing two expiries is not a thing an equity has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
Run on the Windows producer against a cotdata store built by the original producer. Every compared series identical, exit 0, over five symbols spanning an index, an energy, a metal and the two markets whose backadj history goes non-positive. Exact equality rather than a tolerance, since both producers drive the same Norgate install through two code paths. This is what ADR-0007 §7.5 needs before cotdata's price code is deleted, and it was obtainable only while both halves still exist. Two things recorded beyond the verdict. The reconstruction columns matched, which this repo's own docs said to expect drift on. Both producers reconstruct incrementally over their own store's history, so a fresh store recomputing 12,000 bars against one that accumulated them over months looked like a real source of difference. It is not, and the reason generalises: Norgate's historical individual-contract volumes are immutable and the algorithm is identical, so incremental converges on full. That makes --strict-volume usable rather than theoretical. And the two defects the real box found were both invisible to the offline suite — a dependency no extra declared, and a producer-side column family with no consumer-side switch to serve it. A test that cannot install the vendor cannot catch the first, and no offline test calls a parameter that does not exist. The harness caught the second only because it was changed to report the columns it had NOT compared rather than staying silent about them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
CI failed on test (3.10) alone. tomllib is stdlib only from 3.11, and this package declares >=3.10, so the test I added to stop the packaging gap recurring could not run on the floor version — the one most likely to be a stale producer environment, and therefore the one the check is worth most on. Reading the file as text asserts the same fact on every supported version. It is also honest about what the test is: a check that a line is declared in packaging metadata, not a check of TOML semantics. Verified on 3.10.20 as well as 3.11 this time, rather than on the interpreter that happened to be in the venv. That is the same class of mistake as the two the Windows box found — a green suite proving something narrower than it appeared to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jo4iovRfc2fzE9MwcLp7r2
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.
Follow-up to #7, which merged at
340b2ef— the provider alone. As merged,--domain futurescannot run: the provider importsnorgatedataand nothing installs it.These five commits are what the Windows box asked for, and they were written after #7 merged, so they need their own PR rather than being stacked onto merged history.
What the real box found that offline testing could not
Two defects, on the first two contacts, and the pattern matters more than either.
1.
--domain futuresstopped at the import guard. The provider shipped without its dependency:cotdatadeclaresnorgate = ["norgatedata"]and this repo had no such extra. The guard behaved correctly — it refused rather than half-producing — but its message sent the reader to--domain equities, which is right for a Mac or Linux box and exactly wrong for the machine that is supposed to produce futures. A missing package and a Windows-only vendor are two problems with different fixes, and the message now separates them and names the install.2.
get_barshad novolume=parameter. The producer half of volume reconstruction was ported — the provider writesVolume_Reconstructed,FirstVolume,SecondVolume,Volume_Source— and the consumer switch that serves them was not, socotdata'sget_prices(volume="reconstructed")had no counterpart.npf'sml/labels.py:50passesvolume=through, so a repointed call would have raisedTypeError.The docstring carries crowdmon's measurement rather than restating the names, because the names are a trap pointing opposite to intuition:
reconstructedsums exactly two expiries and is 0.52 of total volume in natural gas and 0.54 in crude, sofrontis what a whole-market denominator wants. A test pinsreconstructed < frontso the docstring cannot drift from it. Equities refuse the parameter outright.Neither defect is visible to a suite that cannot install the vendor or call a parameter that does not exist.
The verification, and the harness that produced it
scripts/verify_against_cotdata.pycompares amarketdatafutures store againstcotdata's, reading both stores' parquet directly — no network, no imports of either package except the optional--check-propadj. Exit code gates the promotion. Same posture ascotdata'svalidate_databento_vs_norgate.py: real data, kept out of CI, comparison logic unit tested.The tolerance is stricter than that neighbour's, and deliberately. It compares two independent vendors whose roll calendars and anchors differ, so only shape can agree. This compares one vendor through two code paths, so the passthrough columns must be exactly equal.
Result on the Windows producer, 2026-08-09:
49,892 rows per tier, both tiers, plus contract specs for all five, exit 0. That is what ADR-0007 §7.5 needs before
cotdata's price code is deleted, and it was obtainable only while both halves exist.The harness reported silence as agreement, which is how defect 2 surfaced
It printed reconstruction columns only when they differed, so "compared and identical" and "never compared" rendered as the same nothing — a PASS could not be distinguished from a PASS that skipped half the frame. It now names every column family it skipped and prints the ones that matched. The first real run printed no reconstruction lines at all, and chasing that silence is what found the missing
volume=switch.One expectation corrected
The reconstruction columns were expected to drift, since each producer reconstructs incrementally over its own store's history and
marketdata's was fresh wherecotdata's had months of accumulation. They agree exactly: Norgate's historical individual-contract volumes are immutable and the algorithm is identical, so the incremental path converges on what a full recompute produces. That makes--strict-volumeusable rather than theoretical.Also here
MARKETDATA_STOREon Windows. That box now runs two producers and needs both store roots set at once against different roots — new with the futures domain, and documented nowhere.setxrather thanset, with the reason (a variable living only in the current prompt is the usual cause of a scheduled task failing where an interactive shell works),--checkas the offline confirmation, and an explicit warning against one shared root: each package keeps amanifest.jsonat its own root and does a read-modify-write on it.Testing
119 pass (
-m "not network"), ruff clean, plus the live comparison above. The harness's own tests cover that a single changed bar in 36,000 is caught and dated, that matching NaNs are agreement rather than difference, that a reconstruction difference does not mask a price difference, and that a typo'd store path exits 2 instead of reading as "nothing differed".Generated by Claude Code