From 98d26f0574d4e7699475e2d2e60ef29a92679189 Mon Sep 17 00:00:00 2001 From: test Date: Fri, 31 Jul 2026 10:13:51 +0800 Subject: [PATCH] docs(market-data): the envelope builder states its own reason for returning None, not a claim about what parse accepts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `to_price_feed_envelope`'s docstring justified its `None` by asserting "there is no such thing as an envelope with no prices, and `parse` rightly refuses one". #647 (closing #642) makes an envelope carrying only `fx` valid and accepted, so that borrowed justification is about to be false — a host that can read one public FX rate but not every close can now hand back a rate-only repair for the #612 refusal. The rule itself is unchanged and correct; only the reason given for it was wrong. This lane dates itself from its own rows (`as_of` is `max(row date)`), so with no rows there is no observed session to declare one from — a reason that holds whether or not #647 lands, which is why it is stated that way rather than restating the post-#647 contract on a branch where it is not yet true. Stated as one condition rather than a list of causes, because the claim being replaced went stale by enumerating and a first draft of this one repeated the mistake: it said `None` meant "priced nothing, or the split rule dropped everything", which a bundle whose only priced symbol is a benchmark falsifies — that yields no envelope while having priced something the split rule never touched. Also corrects the same borrowed justification in the mirroring test docstring. Behavior is unchanged — the diff is provably docstring-only (ASTs are identical to origin/main once docstrings are stripped). Flagged while in here and filed as #648: a currency rate the bundle did resolve is discarded along with the envelope, because `fx` is assembled below the early return. Co-Authored-By: Claude Opus 5 --- skills/fomo-kernel/engine/market_data.py | 28 +++++++++++++++++++++--- tests/test_market_data.py | 9 +++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/skills/fomo-kernel/engine/market_data.py b/skills/fomo-kernel/engine/market_data.py index f5d3c0ed..f81555dd 100644 --- a/skills/fomo-kernel/engine/market_data.py +++ b/skills/fomo-kernel/engine/market_data.py @@ -822,9 +822,31 @@ def to_price_feed_envelope(bundle, currency_by_ticker=None, instruments_only=Tru ``currency_by_ticker`` is required for any ticker that is not USD: ``price_feed.currency_conflicts`` compares this against the trades, and - guessing here would either invent a conflict or hide one. Returns ``None`` - when the bundle priced nothing — there is no such thing as an envelope with - no prices, and ``parse`` rightly refuses one. + guessing here would either invent a conflict or hide one. + + **Returns ``None`` when no instrument row survived** — nothing the bundle + priced was both inside this envelope's scope (a benchmark is outside it + unless ``instruments_only`` is false, so a bundle whose only successes are + benchmarks yields no envelope) and reconcilable onto one split basis by the + rule above. Stated as one condition rather than a list of causes on purpose: + the claim this paragraph replaces went stale by enumerating. That is this + lane's own rule and not a restatement of what ``price_feed.parse`` will + accept: this envelope is defined over instrument closes and dates itself + from them, since ``as_of`` is ``max(row date)`` read off the rows themselves, + so with no rows there is no observed session to declare one from. Nothing is + concealed by returning ``None`` — a dropped instrument is already recorded as + a gap on the bundle before this point, so the coverage fact outlives the + envelope, and the caller degrades to the pre-#605 unpriced answer rather than + raising into a review. + + One consequence worth naming, because it is not obvious from the return + value: a currency rate this bundle *did* resolve is discarded along with it, + as ``fx`` is assembled below this point. A book whose held tickers all fail + to price while its currency pair resolves therefore reaches ``consider`` + rateless, even though the bundle counts as ``usable`` on the strength of that + very rate. Whether such a rate should be able to travel on its own is a + question about this lane, open at the time of writing; it is not answered by + what ``parse`` does or does not accept on the other side. """ currency_by_ticker = currency_by_ticker or {} skipped = [] diff --git a/tests/test_market_data.py b/tests/test_market_data.py index 7d2cffd8..1c09d141 100644 --- a/tests/test_market_data.py +++ b/tests/test_market_data.py @@ -897,9 +897,12 @@ def test_i_the_declared_currency_is_the_callers_not_a_default(): def test_i_a_bundle_that_priced_nothing_yields_no_envelope(): - """`parse` rightly refuses an envelope with no prices, so the builder must - return None rather than hand one over: the caller then degrades to the - pre-#605 answer instead of raising into a review.""" + """This envelope is defined over instrument closes and dates itself from + them — `as_of` is `max(row date)` — so with no rows there is no observed + session to declare one from, and the builder returns None rather than hand + over an envelope it cannot date: the caller then degrades to the pre-#605 + answer instead of raising into a review. That is this lane's own rule, not a + claim about which envelopes `price_feed.parse` accepts.""" with provider() as p: bundle = p.resolve(_request(instruments=["DEAD"], benchmarks=[], currencies=[])) assert market_data.to_price_feed_envelope(bundle) is None