Task: Build the FX price feed screen

Table of Contents

This page documents a task in the Implement the synthetic market data journeys in ores.web story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

A quant composes an FX feed and sees the paths it implies before saving it.

2. Status

Field Value
State DONE
Parent story Implement the synthetic market data journeys in ores.web
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-09

3. Acceptance

  • A whole number in a double field decodes on the server, so a weight of 1 saves and a mean of 0 previews.
  • Market data is an area of the menu, drawn for a person who reads the feeds, with an FX price feeds card.
  • The feed list reads one page at a time, searched and ordered on the server.
  • A feed page shows its details, its mixture and its history. A writer adds, edits, reverts and deletes a feed; a reader sees no change offered.
  • The Price behaviour tab, named as in Qt, edits the Gaussians as one set, shows the weight sum, the return distribution and a batch of sample paths drawn from the values on the screen, and saves the set in one call.
  • The mixture of one feed is read by its parent, never by reading every component.
  • Each feed shows a Running or Stopped label, on the list and on its page. A writer starts or stops one feed from its page, and the chosen feeds of the list in one action; each refusal is named with the server's reason.
  • A Ticks tab plots the last 50, 200 or 500 ticks of a feed, with the last price and the move over them, read again every two seconds while it runs.
  • The Price behaviour tab explains the engine, each column and each row in the engine's own terms, warns that the arithmetic engine can go below zero, offers volatility profiles, and gives a mean reverting feed its half-life and band. The engine, tick rate, price source, enabled and auto start fields carry help in the form.

4. Plan

The user chose to do the whole of it in this task, the codec and the gaps the screen needs included. Each step ends in a check.

  1. Codec. A reflect-cpp overlay port whose msgpack reader reads either integer kind into a floating-point field. Checked by a wire codec test that decodes 1 and -2 into doubles, and live by a save with a weight of 1.
  2. Model. fx_spot_config_id on the GMM component is :filterable:, which gives the list an equals and a one-of filter on the feed. The feed's pair, source name and ORE key are :searchable: and :sortable:, and its tick rate :sortable:. Regenerated at every address.
  3. BFF. The synthetic table declares search, sortable and parentField. GET /api/synthetic/:resource reads one page with the total when it is given a limit, and GET /api/synthetic/:resource/by/:parent reads one parent's rows. The page query schema moves to the shared helpers.
  4. Shared parts. A record store (refdata/store.tsx) gives the shared parts their routes through a context, refdata by default. RecordDialog gains complete and check; a record field shows its label.
  5. Screens. The Market data area, the feed list, the feed page and the mixture tab, with the mixture rules in a pure module and an SVG line chart.
  6. Words. English, French and Portuguese.
  7. Documents. The journey, the record screen standard and the menu structure; the codec capture resolved; the gaps task told what landed.

After the first test the user asked for what the Qt market simulator had and the screen lacked:

  1. Ticks. The ticks are published on synthetic.v1.ops.tick.<source>; the stored observations need the feed's series identity, which Name a feed by its oresmd URI brings. So the BFF keeps the newest 500 ticks of each running feed it hears of, as Qt does, and serves them at GET /api/synthetic/feeds/:id/ticks after reading the feed as the session.
  2. Runs. A status label from GET /api/synthetic/running, read every five seconds; Start and Stop on the feed page; row choice and Start chosen and Stop chosen on the list. Start all and Stop all for a collection, through the folder routes, belong to the collection screen task.
  3. Quant help. The Qt editor's words, ported: the engine, the columns and the rows per engine, the arithmetic warning, the profiles and the OU half-life and band. The Qt simple mode, sliders for drift, volatility and jumps, is a second editor for the same mixture and is left for its own task.
  4. Engine. The engine moves from the feed form to the Price behaviour tab, beside the mixture it decides the meaning of. Changing it asks the synthetic service to convert the draft (convert_fx_mixture, with its logic in ores.synthetic.api/feeds/fx_mixture_conversion.hpp), and Save writes the feed's engine and the mixture under one reason.
  5. Login. A login waiting on a party made GET /api/session answer 500, which kept the party picker from showing. It now answers 401.

5. Measurements

Every number this task claims about a tree, with the command that produced it and the commit it was measured at. A number without both is not a measurement, because a clean-up record describes a tree and trunk moves under a branch: a consumer count, a gate's component count and a suite's size all change with no code changing, and the reader of a bare number cannot tell which tree it came from. Where the raw output matters, put it in an evidence/ file beside the task and name that file in the Command column.

What Value Command Commit
Web tests 1090 passed of 1090 npx vitest run in packages/web bf57e9874b
BFF tests 609 passed of 609 npx vitest run in packages/bff bf57e9874b
Conversion tests 5 cases, 22 assertions passed ores.synthetic.api.tests "[fx_mixture_conversion]" bf57e9874b
Codec test whole number into a double passes ores.nats.tests "[wire_codec]" bf57e9874b
Files changed against main 77 git diff --name-only origin/main...HEAD bf57e9874b

6. Notes

7. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

8. PRs

PR Title
#3121 [web] Build the FX price feed screen and the feed screen standard

9. Review

# Comment summary File Decision Notes
1 Query keys of the shared record parts must name their store, or a refdata and a synthetic resource of one name share a cache web/src/refdata/store.tsx Accepted Keys are ['registry', store.name] and ['records', resource, …] with distinct resource names; the shared test renderer seeds the store's key
2 A mixture save is up to three calls (engine, deletions, writes), not one transaction web/src/marketdata/SaveMixtureDialog.tsx Declined No operation writes a feed and its parts together; the journey records the gap and the screen shows the error and reloads. Needs a server apply operation
3 The tick buffer lives in the BFF's memory and starts empty after a restart bff/src/feed-ticks.ts Declined Stored ticks need the feed's series identity; the oresmd task brings the read. The Ticks tab says what it shows
4 Boot auto start never starts a tenant's feeds ores.synthetic/service/src/app/application.cpp Captured Not this branch's code: captured as boot_auto_start_never_sees_tenant_feeds
5 misspell-fixer: "existant" (fr.ts) and "sucesso" (pt.ts) web/src/i18n/locales Declined Correct French and Portuguese, on lines from main
6 Display helpers (shares, half-life, band, profiles, density) compute quant figures in the browser web/src/marketdata/processHelp.ts, mixture.ts Declined for now They decide no written value; the feed screen standard lists them as a gap to move to the server
7 Round 1: the tick buffers are shared by every session while the running read is per caller, so one tenant could unsubscribe another's feeds bff/src/feed-ticks.ts Declined in part The running read is the service's whole process list, so every session states the same set; a test pins that. The real risk it pointed at, a buffer outliving its run and read through another tenant's feed of the same name, is fixed: a run's ticks go when it stops
8 Round 1: buffers never free memory bff/src/feed-ticks.ts Accepted Buffers end with their run, so they are bounded by the feeds running
9 Round 1: the ticks route starts a listener for a stopped feed bff/src/synthetic.ts Accepted Reading starts no listening; the running read decides
10 Round 1: the OU conversion drops the other components silently fx_mixture_conversion.cpp Declined The screen shows each dropped component as Deleted with Undo and a notice names the conversion
11 Round 1: the weight slot holds κ under OU, and the half-life does not guard its inputs fx_mixture_conversion_handler.hpp, fx_mixture_conversion.cpp Accepted A comment at the call site; kappa_for_half_life refuses a duration or rate that is not positive, tested

10. Result

The FX price feed screens shipped under Market data, and their shape became the Feed screen standard for every asset class.

  • List as dashboard. Paged, searched and sorted on the server, with a Running or Stopped label and start and stop of the chosen feeds.
  • Feed page. Details, Price behaviour, Ticks and History tabs; Start or Stop in the header; the engine in the subtitle; delete stops the feed and removes its mixture first.
  • Price behaviour. The mixture as a read-only list with a dialog per component, Share as a computed column, unsaved changes marked per row and cell, and one Save that asks the reason and offers to restart a running feed. The engine lives here, and changing it converts the mixture on the server through the new convert_fx_mixture operation. The distribution and the sample paths preview the draft.
  • Ticks. A price chart of the last 50, 200 or 500 ticks from a BFF buffer, with the stats strip, high and low, an EMA, CSV export and the process beside it.
  • History. One timeline of the feed and its components, with Revert.
  • Along the way. The msgpack reader takes a whole number into a double; the synthetic models gained the parent filter and the feed search; the shared record parts take a store; a pending login no longer breaks the session read; the Gemini review of the ticks is filed and read.

Every Acceptance bullet holds. Left for other tasks: the oresmd identity (Name a feed by its oresmd URI), the simple mode (Give the mixture a simple mode), and collection-wide start and stop (the collection screen task).

Emacs 29.3 (Org mode 9.6.15)