User Journey: Bootstrap or refresh a curve

Table of Contents

This page documents a user journey: A trader builds or refreshes a candidate curve from the day's market, reads its points and its health, and leaves it unpublished.

A user journey describes what one person is trying to do, before anything is built. It is a design document that outlives any sprint, so it names the screens, the entity fields each screen shows, and the operations and messages each step needs.

1. Goal

"I want to turn today's market into a curve I can check before anyone publishes it." The journey is done when the candidate's points answer every pillar of the blueprint and the person can see which market points it read, and nothing has been published.

2. Actor and trigger

The actor is a trader who holds the market-data management role. The trigger is the start of a trading day, a material market move, or a correction to the curve's inputs. The journey starts at a blueprint and an as-of time, and ends with a candidate curve the person has read and not published.

The candidate is a proposal, not an object. It is what the person reads and tests before the publish journey asks a checker to approve it. Nothing the person sees here reaches pricing: pricing reads only a published curve.

3. Steps

  1. Find the blueprint. The person opens Market data, then Curves, chooses a currency and selects a blueprint. The system shows the blueprint: its family role, its output series, its discount curve and its pillars in order.
  2. Choose the as-of. The person names the snapshot time the build reads, with now as the default. The system shows what the market holds for the blueprint's pillars at that time: which points exist, their age and their source. It reads them by the as-of the person names. An observation may come from a market feed or from the synthetic generator; the build treats both as a point with a source, so a generated rate is marked generated rather than observed, and the same bootstrap serves either.
  3. Run the bootstrap. The person chooses Compute candidate. The system calls marketdata.v1.ops.compute_curve, which bootstraps the config's pillars from the observations at that as-of and replies with the computed points: a point id, a date and a discount factor. Nothing is written.
  4. Read the points. The system shows the computed pillars in short-to-long order with their discount factors, beside the market value each came from. The person checks the shape of the curve and the ends.
  5. Read the health. The system shows a health panel: how many pillars the build computed, which input points were missing, which were stale, and whether the discount curve a projection needs was present. The server returns no diagnostics, so the screen derives all of this from the point list and the pillars it read.
  6. Refresh. The person changes the as-of, or corrects an input and runs Compute candidate again. The compute is stateless, so a re-run replaces the points on the screen and writes nothing.
  7. Leave the candidate unpublished. The person leaves the screen without pressing anything that writes. The candidate exists on the screen only; see What is missing.
  8. The publish act, in a later journey. A publish is a named act with an owner and, where the desk configures it, an approval. The operation that exists today, marketdata.v1.ops.republish_curve, writes the bootstrap's observations and lineage straight into the output series and stamps it. It has no candidate step and no approval gate, so this journey does not offer it.

4. Screens and wireframes

Three screens, one build. Result shows the observations the build read and the pillars it solved; Curves shows the input rate, the zero rate, the discount factor and the forward; Checks shows the reprice residual, the convergence, and the missing or stale inputs. All three read the same build, so the curve never differs between them — only the question the reader is asking does. The blueprint and the as-of sit above all three.

wireframe_bootstrap-or-refresh-a-curve.png

Figure 1: Curve bootstrap: the blueprint and as-of, the candidate points, and the health of the build, with the candidate unpublished.

5. Prototype

A static page at doc/prototypes/market_data/bootstrap-or-refresh-a-curve/index.html: mock data only, no backend, no build step and no session. It argues that a build produces a candidate, not a curve the system uses: the observations, their provenance and the health read together, unpublished.

Screens
?variant=a Result (observations and solved pillars), ?variant=b Curves (the four curves), ?variant=c Checks (residual, convergence, missing and stale)
Input source
?source=real market observations, ?source=synthetic a generated feed; the same bootstrap either way, so only the provenance differs
States
clean, missing (inputs absent), projection (a projection whose discount curve is absent)
Source
doc/prototypes/market_data/bootstrap-or-refresh-a-curve/index.html
Open
/OreStudio/doc/prototypes/market_data/bootstrap-or-refresh-a-curve/ once the site is built, or the file directly

6. Entities composed

Entity What it contributes Model
ir_curve_bootstrap_config The recipe the build reads: currency, output series, family role, discount curve, interpolation, day count, split tenor ores.refdata.ir_curve_bootstrap_config
ir_curve_bootstrap_pillar The pillar list the build answers, and its order ores.refdata.ir_curve_bootstrap_pillar
market_observation The input market points at the as-of, and the rows a republish writes ores.marketdata.market_observation
market_series The output series, and the derivation stamp a republish writes on it ores.marketdata.market_series
observation_lineage The per-point provenance a republish writes: config, version, source as-of and source series ores.marketdata.observation_lineage

The computed candidate points are not an entity. They are a reply and a screen; the only curve the data can hold is the one a republish writes.

7. Operations and messages

Step Operation Subject Status
Find the blueprint List or read a blueprint refdata.v1.ir_curve_bootstrap_configs.list, .get exists
Find the blueprint List its pillars refdata.v1.ir_curve_bootstrap_pillars.list exists
Choose the as-of Read observations at an as-of marketdata.v1.market_observations.list exists
Choose the as-of Read one series' observations marketdata.v1.market_observations.list_by_series_id exists
Choose the as-of Read a snapshot of the series marketdata.v1.curve_snapshot.get exists
Run the bootstrap Bootstrap the curve and echo its points marketdata.v1.ops.compute_curve exists
Refresh Bootstrap again at a new as-of marketdata.v1.ops.compute_curve exists
Read the health Read per-pillar diagnostics none missing
Read the evolution Read the curve over a range marketdata.v1.ops.get_series_evolution exists
Leave unpublished Store the candidate none missing
Publish later Bootstrap and publish the output marketdata.v1.ops.republish_curve partial
Publish later Ask for the publish to be approved inbox.v1.ops.raise_approval partial
Read the trail Read every version with its diff marketdata.v1.history.get exists

The two curve operations exist server-side: the handlers and the subjects are in the tree. No caller sends them. The history request is the component's generic one, marketdata.v1.history.get, with entity_type ores.marketdata.market_series or ores.marketdata.observation_lineage; both register a history provider. market_observation registers none, because it carries no audit columns, so a point has no readable trail.

8. What is missing

  1. Nothing calls the curve operations. marketdata.v1.ops.compute_curve, .republish, marketdata.v1.curve_snapshot.get and .buckets have subjects and handlers, and no caller sends any of them. There is no web page, HTTP route or shell verb for a curve operation. The candidate is a page and a route, and a verb for the shell. Until then the whole journey runs only from the shell, and today no command reaches it.
  2. There is no candidate curve. compute echoes its points in the reply and touches no table; republish writes the results straight to market_observation. Nothing sits between them: no candidate entity and no candidate state. So a candidate that is "not yet published" exists only on the screen, and the person cannot return to it, compare it, or hand it to a checker. The candidate is a candidate object, or a candidate state on the output series. Until then the screen holds the points for its own session and says so.
  3. republish publishes with no approval and no named publisher. Its own model comment calls the review and approval gate "a separate, later layer on top of this always-auto-publishing mechanism". No approval kind covers a curve publish: the only concrete kind is iam.role_grant. Publishing is meant to be a named, attributable act; today the operation is anonymous and immediate. The candidate is a market-data approval kind, with how many approvals it needs, the group that decides and the comment it requires, and a publish that waits for it. Until then a screen must not offer republish.
  4. A candidate has no marker. market_series carries derivation_kind, derivation_config_id and derivation_config_version, and a republish stamps them; before that the series reads OBSERVED, exactly like a series the market sent. Two series that differ only by being proposed or observed look the same. The candidate is a stamp at candidate creation, not at publish. Until then the screen labels the candidate itself.
  5. The build reports no health. compute returns success, a message and the points. It does not say which pillar failed, which input was missing or stale, or where it interpolated across a gap. The candidate is a diagnostic per pillar in the response. Until then the screen derives the health panel from the points it received and the pillars it read, and guesses at the causes.
  6. The reads cannot name a time. curve-snapshot.get is always now: it takes no as-of. curve-snapshot.buckets is fixed at five half-hour buckets ending now, unaligned to any observation time. So the person can name a time on the compute and cannot read that time back. The candidate is an as-of parameter and a range on the read. Until then the screen shows only the compute's reply and the series' latest state.
  7. The curve has no typed identity at the wire. The build is keyed by bootstrap_config_id and the reads are keyed by the oresmd URI. Nothing names the curve itself. The candidate is a typed curve identity. Until then the screen joins the config to its output series by the id it read.

9. Related journeys

10. Related knowledge

11. See also

Emacs 29.3 (Org mode 9.6.15)