User Journey: Watch the market and clear its exceptions

Table of Contents

This page documents a user journey: A trader watches the raw market observations arriving for a scope and clears the points that fail: missing, stale or crossed, with a recorded fallback.

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 see what the market is sending and clear anything that is wrong before a curve is built from it." The journey is done when every point in the scope is either clean or carries a resolution the person chose: carried forward, proxied, or escalated.

2. Actor and trigger

The actor is a trader who holds the market-data management role, with operations alongside them for the feeds. The trader watches and decides; both run the same screen. The trigger is the start of a trading day, a feed gap, a stale-data alarm, or the minutes before a curve is bootstrapped. The journey starts at one watch screen and ends when the person leaves it.

The watch and the clearing are one screen. Watching alone is not the goal: the person watches in order to clear, so a separate list and a separate decision form would split one act across two pages.

An observation is evidence, not a signed object. Nobody approves it. This journey clears it: it decides which observations the build may read. Because the resolution is not stored today, the journey carries no server state; see What is missing.

3. Steps

  1. Choose a scope. The person opens Market data, then Market watch, and chooses a scope: a source, a provider, or one series. The system shows the raw observations of that scope, newest first, with the observation time, the oresmd URI, the value, the source and the age. The scope is not typed: the person picks a source from the sources list or a series from the series list.
  2. Read the raw points. The person reads the values, the source channel and the age of each point. The system marks a point fresh, or over the age threshold the screen itself holds. No quality is stored, so the screen marks what it can compute.
  3. Find the exceptions. The system groups the points that fail, three kinds at once: missing, a point the blueprint's pillars name at this as-of with no observation; stale, an age past the threshold; and crossed, a value that inverts the points around it. The screen computes all three from the points and pillars it read. None of the three is computed or stored by the server.
  4. Decide each exception. For each one the person chooses a fallback: carry forward the last good value, proxy it from a named other series, or escalate it to a person. No operation carries the choice; it lives on the page.
  5. Record the resolution. The screen shows each resolution with the person, the time and a reason. If the person carries a value forward, the screen can write that one value through the generated observation put. That write carries no actor, no reason and no link to the exception, so it is indistinguishable from a market point; the resolution itself is not recorded.
  6. Check what the market already holds. The person opens one point's history, or a derived point's lineage, to see its versions and which config produced it. Neither read names a person, because the observation has no audit columns.
  7. Leave. The person goes back to the scope. Nothing cleared is stored, so the next reader sees the same exceptions.

4. Screens and wireframes

One screen. The scope sits at the top, the raw points sit in the middle, and the exceptions and their resolutions sit at the bottom, so watching and clearing read as one act.

wireframe_watch-the-market-and-clear-its-exceptions.png

Figure 1: Market watch: the scope, the raw observations with their source and age, the exceptions, and the fallbacks the person chooses.

5. Prototype

A static page at doc/prototypes/market_data/watch-the-market-and-clear-its-exceptions/index.html: mock data only, no backend, no build step and no session. It argues that the market's exceptions are cleared where they are seen, so the quotes and the fallbacks belong on one screen.

Variants
a exceptions beside the quotes, b below them, c queue-first
States
clean, crossed, stale
Source
doc/prototypes/market_data/watch-the-market-and-clear-its-exceptions/index.html
Open
/OreStudio/doc/prototypes/market_data/watch-the-market-and-clear-its-exceptions/ once the site is built, or the file directly

6. Entities composed

Entity What it contributes Model
market_observation The raw points: time, oresmd URI, value, source and party; the only identity is the URI ores.marketdata.market_observation
market_data_source The source scope: which adapter produced the points, and its kind  
market_series What each point belongs to, and whether the series is observed or derived ores.marketdata.market_series
market_fixing The fixings a scope watches alongside the quotes ores.marketdata.market_fixing
observation_lineage The provenance of a derived point, which the person reads when a value looks wrong ores.marketdata.observation_lineage
ir_curve_bootstrap_pillar The points a blueprint expects, which the screen compares against what arrived ores.refdata.ir_curve_bootstrap_pillar

The observations are append-only and carry no audit columns; the readings that say who cleared what do not exist yet.

7. Operations and messages

Step Operation Subject Status
Choose a scope List the sources marketdata.v1.sources.list planned
Choose a scope List the series marketdata.v1.market_series.list exists
Read the raw points List one series' observations marketdata.v1.market_observations.list_by_series_id exists
Read the raw points List observations with an as-of marketdata.v1.market_observations.list exists
Read the raw points List the fixings marketdata.v1.market_fixings.list exists
Read the raw points Read each point's lineage marketdata.v1.observation_lineages.get exists
Find the exceptions Compare against the expected points refdata.v1.ir_curve_bootstrap_pillars.list partial
Find the exceptions Check where each feed starts synthetic.v1.ops.calibrate_feeds partial
Find the exceptions Read the exceptions of a scope none missing
Decide each exception Read the exception rules and thresholds none missing
Record the resolution Write the resolution none missing
Record the resolution Write a carried-forward value marketdata.v1.market_observations.put partial
Escalate Raise an approval request inbox.v1.ops.raise_approval partial
Check what the market holds Read every version with its diff marketdata.v1.history.get exists

The history request is the generic one, with entity_type ores.marketdata.market_observation or ores.marketdata.observation_lineage; both register a history provider. The calibration read covers only the FX asset class, so the screen cannot ask it about every feed.

8. What is missing

  1. An observation has no quality and no rejection. The columns are id, party_id, series_id, observation_datetime, oresmd_uri, value and source. There is no status, quality flag, rejection reason or rejection author. The candidate is a quality value and a rejection record, or a companion exception row. Until then the screen marks points from the values and the ages it computes itself, and nothing is stored.
  2. There is no exception. No subject raises, lists or resolves an exception, and no entity holds one. The candidate is an exception entity with its scope, its kind, its state and its resolution, and the subjects to list and resolve it. Until then the screen computes the three kinds in the page and shows them read-only.
  3. The scope is not typed. A scope is a series UUID or a source string. Nothing names "the USD rates quotes", "the funding curve's inputs" or "this feed's points" as a scope the server resolves. The candidate is a typed scope, or a filter over asset class, currency, instrument and curve family. Until then the person picks a series from the series list or a source from the sources list.
  4. A resolution is not recorded. A fallback choice has no subject and no fields. A carried-forward value can be written, but the write carries no actor, no reason and no link to the exception it answers, so the record of who cleared what and why does not exist. The candidate is a resolution operation that names the exception, the choice, the person and the reason. Until then the screen shows the choice for the session only.
  5. The thresholds and the rules are not stored. Nothing holds the age at which a point is stale, or what counts as crossed for a given series. The candidate is an exception policy, per series or per blueprint. Until then the screen holds one threshold for every scope.
  6. A point carries a source id. market_observation.source_id is a typed reference. The screen joins it to the sources list to show the name and the kind. A point whose source is missing from the list shows as unknown.
  7. No screen exists. No web page, HTTP route or shell verb reaches any curve or watch operation. The candidate is a page and a route. Until then the journey runs only from the shell.

9. Related journeys

10. Related knowledge

11. See also

Emacs 29.3 (Org mode 9.6.15)