Feed-owned composites

Feed-owned composites

A dated futures contract eventually expires, but most strategies want one logical series spanning many expiries. Marketfeed builds that series: it elects every root’s rolls and serves them as a roll schedule (GLE-270), and it splices and adjusts the held contracts into one :cont composite that the engine consumes as served (GLE-323). The engine elects nothing and adjusts nothing; it checks the two served objects against each other, drives the strategy over the composite and translates orders onto the dated contracts.

A continuous run symbol takes one of two forms (GLE-325, owner Decision 4), and both consume the served composite. A run can name several roots; each is stitched by its own served schedule and adjusted by its own method, and no root may appear twice (GLE-424):

  1. Bare root. fut:XCME:ES: the engine takes the venue and product from the symbol and adjusts with Panama (panama_snapshot_forward).
  2. Named composite. fut:XCME:ES:cont:adj=<panama|ratio|none>: the same root with the adjustment method named on the symbol, as an equity names :adj=splitdiv. A bare fut:XCME:ES:cont is adj=panama.

In both forms the price grid (tick size and point value) comes from the adapter’s instrument metadata for the root, read once at startup; the engine keeps no symbol registry of its own. The grammar is strict: :cont must follow the root directly, adj= may appear once, and the symbol carries no ofs= (ofs=B1..B3 is viewer-only in marketfeed; ofs=F is refused as a qualifier), no straddle= (the straddle policy is the run clock’s) and no @ suffix (the session is the run’s, --session). Each violation has its own refusal, before any adapter spawn; see Continuous futures. The @ES aliases of the engine’s former YAML registry no longer resolve: since ER5 (GLE-327) an @-prefixed series is no continuous form and takes the plain path, where the adapter judges it (ER3 had refused it with the replacement named, for one release).

Either way the adapter must serve ROLL_SCHEDULE, which travels only on the protobuf wire, as does the composite: with no --data-protocol the run asks for pb, and an adapter that cannot serve the schedule fails the run, and --verify, before any bar is fetched. A root the feed serves no schedule for fails loudly too (no served roll schedule for <root>); the engine has no election of its own to fall back on. A daily run (1d/1w) also needs the adapter to advertise the trading-day session it reads (eth when the series names none); one without it is refused after the schedule check, and the refusal names --continuous-clock epoch, which reads UTC epoch buckets instead.

Run a root against a marketfeed adapter with:

bin/algo run \
  --adapter "$MF_ADAPTER" \
  --adapter-inputs Dir=/usr/local/marketfeed \
  --data-protocol pb \
  --strategy bin/hello \
  --symbols fut:XCME:ES \
  --interval 1d \
  --start 2022-01-01T00:00:00Z \
  --end 2025-01-01T00:00:00Z \
  --results-dir results

--symbols fut:XCME:ES:cont:adj=ratio runs the same root ratio-adjusted. The run id is run-<slugified symbol>-<interval>: run-fut-xcme-es-1d here, run-fut-xcme-es-cont-adj-ratio-1d for the named composite.

The run consumes the composite in stages:

  1. The engine reads the root’s served roll schedule once, anchored (adjust_anchor) at the run end, for the current generation, the instant of the feed’s newest bar (last_bar_at) and the snapshot token of that state under that anchor. A schedule is a chain of held spans, each naming a dated contract and a half-open interval [Start, End), a seam at every cut instant carrying the outgoing and incoming prices the feed measured there, and, on every span up to the anchor’s holder, the cumulative adjustment constant the feed applied to it. A schedule for another root, one not disclosing its held rank, or one without a snapshot token is refused here, before any bar is fetched. A schedule holding a deferred rank (B1, B2) is followed like any other (Held rank).
  2. It fetches the composite bars once, on the run’s clock (GLE-324, D5 = E): at 1d or 1w, the feed’s session composite fut:<VENUE>:<ROOT>:cont:adj=<panama|ratio|none> under the series’ trading-day session (--session; eth, the exchange trading day, when none is given), one trade-date bar per session, each carrying its BarSession block; at an intraday interval, and at every interval under --continuous-clock epoch, UTC epoch buckets at the run interval with :straddle=incoming. The window runs from the run’s first UTC day to the run end; the request is pinned to the probed generation and anchored at the run end, and when the end stands it pins the probe’s snapshot token too, which gives the fetch a complete identity: the series cache serves it under that token and anchor. An end beyond the feed’s newest bar is clamped to last_bar_at plus one bar (recorded as run_end_clamped), so a far --end is not refused as past the data; the manifest’s snapshot instant stays the configured end, and a clamped fetch pins no token and is read live. The method is the symbol’s adj= (panama, ratio or none; the definition records it as panama_snapshot_forward, ratio_snapshot_forward or unadjusted), Panama when the symbol names none.
  3. When the end stood and the bars came back under the probe’s token, the probe is the schedule the build uses: two wire reads in the common case. Otherwise (a clamped or re-anchored end, or bars served under another token) it fetches the schedule again, pinned to the bars’ token under the same anchor. Unequal tokens (SNAPSHOT_MISMATCH) and a pinned schedule reporting itself superseded (GENERATION_SUPERSEDED: the feed’s state moved between the fetches) fail the run; there is no automatic re-issue.
  4. BuildComposite checks the pair against each other and against the window and takes every adjusted price, raw price and constant from the wire as served. A bar belongs to the span whose interval holds its close (The instant law); its tag must name a span at or before the anchor’s holder, its cumulative_adjustment (or cumulative_price_ratio) must equal that span’s, the holder must carry the identity constant, and every span between the first and the last tagged must have a bar. The engine recomputes no arithmetic: the constant is checked, not the sum.
  5. The strategy receives one logical, adjusted series under the symbol as the run named it (fut:XCME:ES, or fut:XCME:ES:cont:adj=ratio), while the resolver remembers the active raw contract and each contract’s served constant. Orders and fills are translated at the venue boundary, and open exposure is rolled at each cut: both roll legs fill at the cut instant at the served seam prices, each paying the run’s slippage and commission.

The anchor is the contract holding the run end: it carries the identity constant (offset 0, factor 1) and every older contract carries its constant relative to it, so the newest segment sets the price level and older ones are adjusted toward it. When the end falls inside a span that has no bar yet (the first hours after a cut), the bars are fetched again, ended and anchored one nanosecond after the last served bar, and the schedule is pinned under that anchor; the run notes the move. The feed measures each seam under its declared seam policy (r1-settlement@trade-date-boundary/v1: the two contracts’ settlements on the trade day before the roll, falling back to their last common prices when a settlement is missing) and accumulates the constants from it; the engine takes both as served and neither re-measures nor re-sums them.

With --results-dir, a continuous run writes a manifest describing contracts, boundaries, adjustment constants and checksums, plus an audit.jsonl ledger of raw and adjusted fills and rolls. The manifest is laid out as the former local adjuster laid it out (the anchor, back-adjustments from the served constants, forward records from the served pairs), so the two can be diffed, and its self-check is equality with the served manifest, field for field, rather than a local recompute. A degradation.json file appears only when something weakened the run: a degradation flag the feed served for a traded contract, a clamped run end, a provisional span, a straddling bar, or a seam the feed built from a fallback price (traded only under --accept-degraded-seams; the run is refused otherwise). The manifest records the served state it was built from as sched:<recipe>@g<generation>@snap:<token>;rank=<F|B1|B2> on every contract source (;seam=<policy> and ;degraded-seams=accepted follow when they apply). These audit files sit beside the run’s performance artefacts (result.json, equity.csv, trades.csv, report.html) in the same directory; the audit ledger reconciles the continuous stitching while the performance artefacts report its economics.