Task: Move the swap leg's notionals and rates into child rows
Table of Contents
This page documents a task in the Rework the rates instruments to the instrument table rule story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
Give a swap leg's notionals and rates child rows keyed by the leg, populate them from the full ORE lists, and stop the flat columns from standing in for a schedule.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Rework the rates instruments to the instrument table rule |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Nothing. |
| Last touched | 2026-10-08 |
3. Acceptance
- ores_trading_swap_leg_amounts_tbl and ores_trading_swap_leg_rates_tbl exist, keyed by the trade, the leg ordinal and an entry ordinal.
- swap_leg no longer carries notional, fixed_rate or spread; the children carry them.
- map_leg reads every notional and every rate or spread, not the first of each.
- The import writes the children and the export reads them back, so a stepped leg round trips.
- The component regenerates drift-clean and builds, and the codegen suite passes.
4. Plan
The leg's economics become rows of their own and the wire carries them, in five parts.
- Two child models,
swap_leg_amountandswap_leg_rate, keyed by the trade, the leg ordinal and their own ordinal, withrate_rolenaming the arm that stated the rate (fixedorspread). swap_leglosesnotional,fixed_rateandspread: a single column cannot hold a schedule, and keeping both would state one fact twice.map_legstops taking the first of each list;map_leg_amountsandmap_leg_ratesread every entry, including theAmortizationsarm when the document states a schedule instead of a list.- The carrier
swap_instrument_datagains the two collections, and the hand-written payload codec carries them, so the export can rebuild the lists it reads. - The import writes the children after the legs, and the export reads them back through a batch read on each child.
The children are flat, like the instrument_* children beside them: no
identity field group and no audit group, so their domain class is one struct.
5. Measurements
Every number this task claims about a tree, with the command that produced it and the commit it was measured at.
| What | Value | Command | Commit |
|---|---|---|---|
legData members the mapper read, before |
3 of the list arms, first entry only (Notionals, Rates, Spreads) |
read map_leg in swap_instrument_mapper.cpp |
616eb59f95 |
legData members the mapper reads, after |
every entry of all three, plus Amortizations |
the same | working tree |
swap_leg economics columns, before |
3 (notional, fixed_rate, spread) |
sed -n '/^create table/,/^);/p' trading_swap_legs_create.sql |
616eb59f95 |
swap_leg economics columns, after |
0 | the same | working tree |
| New tables | 2 (swap_leg_amounts, swap_leg_rates) |
ls projects/ores.sql/create/trading/trading_swap_leg_*s_create.sql |
working tree |
| Files the unit touches | 99 | git status --short |
working tree |
| ORE mapper round-trip tests | 614 cases, 482159 assertions, all passing | ores.ore.core.tests against a recreated database |
working tree |
6. Notes
The children's tables are new, so their permission codes had to be seeded in
iam_permissions_populate.sql; the handler-permission gate fails without
them. The component's component_files.cmake lists also had to be
regenerated, or the new repository units are never compiled and the link
fails.
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 |
|---|---|
| #2802 | [trading,ore] Move a swap leg's notionals and rates into child rows |
10. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
11. Result
A leg's notionals and its rates are rows of their own, and the import keeps every one of them.
- Two child tables.
swap_leg_amountholds one notional per row, dated from the day it applies;swap_leg_rateholds one rate or spread, withrate_rolenaming the arm of the document that stated it. Both are keyed by the trade, the leg ordinal and their own ordinal. swap_leglost its economics columns.notional,fixed_rateandspreadare gone, because a single column cannot hold a schedule and keeping both would state the same fact twice.- The mapper keeps the whole list.
map_legreadfirst_notional(...),Rates.Rate.front()andSpreads.Spread.front()and dropped the rest; the newmap_leg_amountsandmap_leg_ratesread every entry, and read theAmortizationsarm when the document states a schedule rather than a list. The amortisation's own type, end date and frequency are not carried yet, and the reason is that this row states a value and a start date. - The children cross the wire.
swap_instrument_datagained the two collections and the hand-written payload codec carries them, with a fallback tier for a payload written before the change, because the export rebuilds a leg from the payload and nothing else. - The import writes them and the export reads them back, through a batch read on each child that mirrors the legs' own.
Verification: the component regenerates with no drift; the codegen suite is 1276 passed; the full preset build exits 0 with no errors and no compiler warnings; and the ORE mapper round-trip suite is 614 cases and 482159 assertions, all passing, against a database recreated from this branch. That last one is the proof that matters: the golden IR and swaption documents state stepped legs, the export rebuilds them from the children, and the comparison is byte-level.
Not run: the trading service's eventing integration tests. They need the service fleet, and this environment's user systemd bus is down, so services cannot start. The database was recreated for the mapper suite, which needs no fleet.