User Journey: Check a curve's fit
Table of Contents
This page documents a user journey: A trader or quant checks a candidate curve against the quotes it came from: the quotes overlaid on the candidate, the pillar residuals, the forward-rate smoothness, and the calendar and roll logic.
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 know whether this candidate curve still stands on the quotes it was built from, and whether its forwards and its dates make sense." The journey is done when the person has seen the raw quotes against the candidate, the pillar residuals, the forward-rate smoothness, and the calendar and roll logic, and can say whether the candidate is sound. The person does not publish here.
2. Actor and trigger
The actor is the trader or the quant. The trader owns the candidate; the quant owns the method and reads the same screen. Neither decides to publish here: an approver does that in a different journey. The trigger is a bootstrap or refresh just run, a residual that looks too large, a forward curve that steps, or a pillar that settles on an unexpected date. The journey starts at a candidate curve and ends when the person leaves it, still a candidate.
This journey reads and diagnoses; it changes nothing. It is the check the maker runs before the candidate goes to a second pair of eyes.
3. Steps
- Open the candidate. The person opens Curves and surfaces and selects
the candidate curve. The system reads the
market_seriesrow and its =derivation_kind=/config/version, which says the curve is derived and by which recipe. - Read the candidate points. The person chooses Read candidate. The
system reconstructs the latest snapshot of the curve with
marketdata.v1.curve_snapshot.get, one point perpoint_id, at now. The read has no as-of parameter, so the person cannot look at the candidate as of an earlier instant. - Read the raw quotes. The system lists the quotes the curve was built
from with
marketdata.v1.market_observations.list_by_series_id, one row peroresmd_uriwith its value, source and time. The quotes and the candidate are two separate reads. - Overlay the quotes on the candidate. The person reads each raw quote
against its candidate point. No operation joins the two lists: they come
from different subjects and are keyed differently (the quote by an
oresmd_uri, the candidate by apoint_id), so the screen places them side by side by reading both and matching on the coordinate. See What is missing. - Read the pillar residuals. The person reads the residual at each pillar, meaning the distance between the quote the curve was built from and the rate the curve returns at that pillar. No operation serves a residual; the compute reply carries bootstrapped points and no per-pillar error. See What is missing.
- Read the forward-rate smoothness. The person reads the forward curve
between pillars and looks for a step. A server-side health checker exists
that flags a negative forward rate as
INFOand a leave-one-out outlier asWARNING, but no operation calls it, so nothing reaches the screen. See What is missing. - Read the calendar and roll logic. The person checks how each pillar
tenor resolves to a date and which calendar and roll convention applied.
The system reads the recipe with
refdata.v1.ir_curve_bootstrap_configs.getand its pillars withrefdata.v1.ir_curve_bootstrap_pillars.list, and resolves a tenor throughrefdata.v1.tenor_convention_resolutions.list_by_convention_code. The resolved dates are not stored on a point, so the screen derives them. - Recompute if needed. If the person suspects a stale candidate, they choose
Recompute candidate, which calls
marketdata.v1.ops.compute_curvewith the config and an as-of and returns the bootstrapped points. The operation exists but no caller sends it today. - Leave. The person returns to the list. The candidate is unchanged; the person did not write or publish anything.
The compute and the snapshot reads are server-side and synchronous, so the journey carries no state of its own. Recomputing a candidate returns points; it does not publish them.
4. Screens and wireframes
One screen. The candidate and its as-of sit at the top; the quotes overlaid on the candidate, the residuals and the forwards sit in the middle; the calendar and roll logic sits at the foot, so the person reads the market, then the fit, then the dates it settles on.
Figure 1: Curve fit check: the candidate against its raw quotes, the pillar residuals, the forward-rate smoothness, and the calendar and roll logic.
5. Prototype
A static page at doc/prototypes/market_data/check-a-curves-fit/index.html: mock data only, no backend,
no build step and no session. It argues that a curve is checked against the quotes it came from: residuals, forward smoothness and the roll logic, on one screen.
- Variants
- a the residuals table, b the overlay chart, c both
- States
- fit, large (one pillar's residual is large), gap (a quote the candidate does not join)
- Source
doc/prototypes/market_data/check-a-curves-fit/index.html- Open
/OreStudio/doc/prototypes/market_data/check-a-curves-fit/once the site is built, or the file directly
6. Entities composed
| Entity | What it contributes | Model |
|---|---|---|
market_series |
The candidate's identity and its =derivation_kind=/config/version | ores.marketdata.market_series |
market_observation |
The candidate points and the raw quotes, one per oresmd_uri and observation_datetime |
ores.marketdata.market_observation |
observation_lineage |
The provenance of a derived point: which config/version and which source series it read | ores.marketdata.observation_lineage |
ir_curve_bootstrap_config |
The recipe: interpolation, day count, tenor convention and split tenor | ores.refdata.ir_curve_bootstrap_config |
ir_curve_bootstrap_pillar |
The pillars the residuals are read at: sequence, start and end tenor, role | ores.refdata.ir_curve_bootstrap_pillar |
curve_role |
The pillar's instrument role (deposit, FRA, swap) that decides the pricing derivation | ores.refdata.curve_role |
tenor |
The tenor labels a pillar is written in | ores.refdata.tenor |
tenor_convention |
The scheme that resolves a tenor to a date (spot/forward, FOMC, IMM) | ores.refdata.tenor_convention |
day_count_fraction_type |
The day-count convention applied per pillar | ores.refdata.day_count_fraction_type |
currency |
The currency the pillars are quoted in | ores.refdata.currency |
A residual, a forward-rate finding and a resolved pillar date have no entity today. The screen computes the residual and the forward on the client from the two reads, and resolves the pillar dates from the recipe; see What is missing.
7. Operations and messages
| Step | Operation | Subject | Status |
|---|---|---|---|
| Open the candidate | Read one series | marketdata.v1.market_series.get |
exists |
| Read the candidate points | Read the curve snapshot at now | marketdata.v1.curve_snapshot.get |
partial |
| Read the raw quotes | List the observations of one series | marketdata.v1.market_observations.list_by_series_id |
exists |
| Overlay the quotes | Join the quotes to the candidate points | (no joint read) | missing |
| Read the pillar residuals | Read the fit residual per pillar | marketdata.v1.curve_bootstrap.diagnostics |
missing |
| Read the forward rates | Read the forward-rate findings | marketdata.v1.curve_bootstrap.health |
missing |
| Read the calendar and roll | Read the recipe | refdata.v1.ir_curve_bootstrap_configs.get |
exists |
| Read the calendar and roll | Read the pillars | refdata.v1.ir_curve_bootstrap_pillars.list |
exists |
| Read the calendar and roll | Resolve a tenor to a date | refdata.v1.tenor_convention_resolutions.list_by_convention_code |
exists |
| Recompute the candidate | Bootstrap the candidate without publishing | marketdata.v1.ops.compute_curve |
exists |
| Recompute and publish | Bootstrap and republish | marketdata.v1.ops.republish_curve |
exists |
| Read the recipe lineage | Read the lineage of a derived point | marketdata.v1.observation_lineages.list |
exists |
marketdata.v1.ops.compute_curve and .republish both exist and no
caller sends them today; the preview half is the one this journey would use.
The snapshot read is partial because it always reads now and has no as-of
parameter. The generic history read is marketdata.v1.history.get with entity_type
ores.marketdata.market_series; market_series and observation_lineage
register history providers.
8. What is missing
- There is no residuals or diagnostics operation. The subject
marketdata.v1.curve_bootstrap.diagnosticswould have to be added to return, per pillar, the quote the curve was built from, the rate the curve returns at that pillar, and the residual. Until then the screen reads the candidate and the quotes separately and matches them by coordinate, and it cannot say what the bootstrap engine itself measured. - The health checker has no caller.
curve_health_checkerexists inores.analytics.quantand flags a non-positive discount factor, a negative forward rate asINFOand a leave-one-out forward-rate outlier asWARNING. Nothing calls it outside its tests, and the compute reply carries points only, so no finding reaches the wire. The candidate is a subject such asmarketdata.v1.curve_bootstrap.health, or a findings field on the compute response. Until then the screen plots the forwards and marks that no check ran. - A read cannot be taken as of a time in the past. The snapshot read always
reads now, so the person cannot check the candidate as of the instant it
was built. The candidate is an
as_ofparameter on the snapshot request. Until then the screen reads the current snapshot only. - The quotes and the candidate are not joined. They are keyed differently,
the quote by an
oresmd_uriand the candidate by apoint_id, and no operation returns both. The candidate is an overlay read that returns a quote and its candidate rate together. Until then the screen joins the two lists itself and shows an unmatched quote as a gap. - A pillar's resolved date is not stored. The recipe names a tenor convention, and a resolution read returns the scheme's rows, but the date a given pillar resolved to is not on the point, so the screen must resolve it again and the two can disagree. The candidate is the resolved date on the lineage of a derived point. Until then the screen derives the dates and labels them as derived.
- The curve-fit screen exists for curves only. There is no surface or slice read and no surface residual, so a surface fit cannot be checked on this screen. The candidate is a surface read and a surface residual subject. Until then a surface fit is checked on the concurrent journey Calibrate or refresh a surface, where the same gap is recorded.
9. Related journeys
- Bootstrap or refresh a curve — build the candidate this journey checks.
- Adjust a curve or surface by hand — correct a pillar the residuals flag.
- Watch the market and clear its exceptions — see which raw quote failed before the check.
- Ask for a publish to be approved — send a sound candidate to a second pair of eyes.
- Watch a curve or surface evolve — compare the candidate with the published curve it would replace.
10. Related knowledge
11. See also
- Bootstrapping a curve: from quotes to a published set — the structure note that orders this cluster, and where to read this page in it.
- Market data journeys
- Decide a publish — the approver reads a similar picture before deciding.
- User Journeys