Task: Populate the FX family
Table of Contents
This page documents a task in the Populate the ACME books with a realistic trade population — the remaining work story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
ACME's books hold foreign exchange trades: forwards, swaps, vanilla, barrier and digital options, touch options, Asian options, variance swaps and the rest of the sixteen routed types. The family carries 25% of the population. Every trade names two currencies, so the planner draws a pair whose spot and forward points the tenant holds.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Populate the ACME books with a realistic trade population — the remaining work |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-24 |
3. Acceptance
- Every FX trade type the catalogue routes books a trade.
- The ten unrouted types are routed or named out of scope.
- The class share of the population matches its weight within tolerance, and every routed type appears at least once.
- The family exports as the ORE portfolio the engine reads, and reverse-maps without loss.
- The report that prices the family runs against the spot rates, forward points and pair volatility surfaces it needs, or the task states which pricing it cannot prove yet.
4. Plan
4.1. What the tree already holds
- The routing. Sixteen types reach seven tables:
fx_forward_instrument(FxForward, FxSwap),fx_vanilla_option_instrument(FxOption),fx_digital_option_instrument(FxDigitalOption, FxTouchOption, FxDoubleTouchOption, FxDigitalBarrierOption),fx_asian_forward_instrument(FxAverageForward, FxTaRF),fx_barrier_option_instrument(FxBarrierOption, FxDoubleBarrierOption, FxEuropeanBarrierOption, FxGenericBarrierOption, FxKIKOBarrierOption),fx_variance_swap_instrument(FxVarianceSwap) andfx_accumulator_instrument(FxAccumulator). - The writer, service side.
write_instrument_batchwrites every one of those arrays, so no trading service change is needed. - The vocabulary. The currency set is seeded and holds 179 currency pairs, each with its convention calendars.
4.2. How FX differs from rates, and why that matters
A rate trade was one header plus one fact row plus legs, and the store rebuilt it from the pair. An FX row is the whole instrument: the routed table carries the trade, the parties, the currencies and the economics in one row, so there is no fact row to forget. The lesson the rate family learned the hard way does not repeat here, and the writer is simpler for it.
That simplicity costs variety instead. The sixteen types fall into three idioms, and a payload has to state all three:
| Idiom | Types | What it states |
|---|---|---|
| bought and sold | FxForward, FxSwap, FxOption, the five barrier options | a bought currency and amount against a sold one, a date, and for the options a type and an exercise style |
| digital | FxDigitalOption, the touch options, the digital barrier | a foreign and a domestic currency, a payoff currency and amount, a strike, a side, and barriers where the type has them |
| index and notional | FxAverageForward, FxTaRF, FxVarianceSwap, FxAccumulator | an underlying index, a reference notional, a fixing or strike, a side, and the barrier an accumulator knocks out on |
4.3. Decisions
- D1 — one payload, three idioms.
fx_termscarries the union of the fields the seven tables state, most of them optional, in the waycommodity_termsalready does for its eight shapes. The writer dispatches on the trade type to the table the router names and fills that table's fields. Splitting the payload into three variants would put the routing decision in the type system and then repeat it in the writer anyway. - D2 — the pair comes from the catalogue. A pair is the tenant's reference data, not this configuration's, so the scope carries the pairs the tenant holds and the planner draws from them. It prefers the pairs that name the book's own currency, because a desk books what it quotes; a tenant holding none of those falls back to the whole set rather than refusing.
- D3 — every shape gets an export case. The rate family shipped one export test against its simplest product and two of its products exported wrong data. Here the seven tables get seven cases, and the review of this task does not begin until they exist.
4.4. Steps
- The payload.
fx_termsand its variant entry, plus the pair list on the scope. - The planner.
fx_trade_plannerregistered underfx, cycling the sixteen routed types, drawing the pair, the dates and the economics that type needs. - The writer. One arm per routed table, filling the fields the table states and no others.
- The profile. A config for an entity whose books quote FX, so a provisioning run produces the family.
- The proof. Seven export cases, one per table, each asserting the document names the instrument and its economics; then a population run measuring the type coverage and the share.
- The record. Measurements, Result, and the gaps this task cannot close.
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 |
|---|---|---|---|
6. Measurements
| What | Value | Command | Commit |
|---|---|---|---|
| The build | exit 0 | compass build |
d81a25f361 |
| The codegen gate | no drift | check_component_drift.py --all |
d81a25f361 |
| The cmake list gate | exit 0 | regenerate_cmake_component_files.py --all --check |
d81a25f361 |
| The provisioning run | 9 of 9 steps | compass shell -l ...acme...ores |
d81a25f361 |
| FX trades booked | 16, one per routed type | select count(*) from ores_trading_trades_tbl where trade_type like 'Fx%' |
d81a25f361 |
| Distinct FX types | 16 | select count(distinct trade_type) ... |
d81a25f361 |
| Range bounds | 1, for the accumulator | select count(*) from ores_trading_range_bounds_tbl |
d81a25f361 |
| The ORE suite | 475,472 assertions in 393 cases | ores.ore.core.tests |
d81a25f361 |
| The synthetic suite | 1640 assertions in 74 cases | ores.synthetic.core.tests |
d81a25f361 |
| The export cases | 7, one per routed table | ores.ore.core.tests [ore][xml][exporter] |
d81a25f361 |
7. Notes
8. 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 |
|---|---|---|
9. PRs
| PR | Title |
|---|---|
10. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
11. Result
12. Result
The FX family books. A provisioning run completes all nine steps and writes sixteen FX trades, one of every type the router sends to an FX table: a forward, a swap, a vanilla option, the five barrier products, the four digitals, an average forward, a target redemption forward, a variance swap and an accumulator. Each states the pair the tenant's catalogue holds, preferring the pairs that name the book's own currency, which for a GBP book means GBP crosses.
A trade states one of three idioms, and the payload carries all three with the fields a type does not use left empty: a bought currency against a sold one, a digital payoff struck against a foreign and a domestic, and an index written on a reference notional. An FX row is the whole instrument, so there is no fact row to forget — the defect that cost the rate family a review cycle cannot repeat here.
Seven export cases, one per routed table, prove the document carries the instrument and its economics. Two of them assert what ORE does not carry, and that is the finding: an FxOption states no exercise style because ORE implies European, and a plain barrier states no underlying because the pair is implicit in its two currencies. A third found a real gap: the document refuses an accumulator with no range bound, and the bound is a child row rather than a column, so the writer now states one.
What this does not claim: the class share is a story level check and is not met while only bonds, rates and FX are populated; and the whole portfolio the ORE service gathers for a run is still unproven, as it is for every family so far.