Task: Write the swap legs on import and record their direction
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
Make the swap legs the importer builds reach the database, keyed by the trade and the ordinal as bond_legs is, with the leg's direction recorded.
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_legs_tbl is keyed by (tenant_id, trade_id, leg_number) with no surrogate id, and carries payer.
- swap_instrument_mapper::map_leg reads legData/Payer.
- The import writes every leg of a swap-shaped trade after its instrument, and fails the item when a leg write fails.
- The component regenerates drift-clean and builds, and the codegen suite passes.
4. Plan
The importer already builds the legs and stamp_ids already stamps them, so
the work is two small changes: the leg gains the direction ORE requires, and
the import writes the legs it already builds.
payerjoinsswap_leg,map_legreadslegData/Payerandreverse_legemits it, so the direction survives import and export both.- The handler writes each leg after its instrument, and fails the item when a leg write fails. The leg's id is minted at the write site, because the table still keys the leg by its surrogate.
The re-key to (trade_id, leg_number) was attempted and set aside. It
surfaces a codegen defect: the compound-key path cannot generate a compilable
eventing integration test, because cpp_nats_integration_test.cpp.mustache
inserts each extra key column's raw domain value where the generated service
takes every key part as text. Fixing that template belongs with the codegen
work, so the re-key was recorded as a capture and the surrogate key stood here.
The codegen defect is fixed and the re-key landed in
Re-key the swap legs to the
trade.
The amount and rate children that carry an amortising notional and a stepping
rate are the next unit: map_leg reads only the first notional, rate and
spread today, so the children would be empty until the mapper reads the lists.
The database is recreated rather than migrated (recreate_is_the_only_db_migration).
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 |
|---|---|---|---|
payer column on the leg, before |
absent | sed -n '/^create table/,/^);/p' trading_swap_legs_create.sql |
02da314ddc |
payer column on the leg, after |
present, boolean null |
the same | working tree |
swap_legs primary key |
unchanged, (tenant_id, id, valid_from, valid_to) |
the same | working tree |
legData members with no destination, before |
Payer and the amortisation, payment-date and settlement groups |
external/ore/xsd/instruments.xsd against the create script |
02da314ddc |
ORE mapper lines that read Payer, before |
0 | grep Payer swap_instrument_mapper.cpp |
02da314ddc |
ORE mapper lines that read Payer, after |
2, one per direction | the same | working tree |
6. Notes
The swap_leg row keeps its :default: 1 syntax on leg_number rather than
the :default_value: its siblings use. It emits the correct SQL default, so
it is left alone here rather than changed in an unrelated diff.
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 |
|---|---|
| #2795 | [trading,ore] Write a swap's legs and record their direction |
9. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
10. Result
The swap legs the importer builds now reach the database, and a leg records which side pays.
- The direction is recorded both ways.
payerjoinsswap_leg,map_legreads ORE's requiredlegData/Payerandreverse_legemits it. Before this, even a mapper that built the legs dropped the direction, and the stored shape had nowhere to put it. - The import writes the legs. The handler's
swap_instrument_databranch wrote the instrument and its call dates and dropped the leg vector thatstamp_idshad already stamped with the trade and the activity. It now writes each leg after its instrument and fails the item when a leg write fails, so a stored swap is an instrument rather than a row. This is finding R15 of the family design, and it is why a swap imported before this change exported with no legs at all. - The re-key was attempted and set aside. Dropping the surrogate
idfor(trade_id, leg_number)fails to compile a generated test: the compound-key path inserts each extra key column's raw domain value where the generated service takes every key part as text, so anintordinal lands in astd::vector<std::string>. The fix is a template change that belongs with the codegen work, so the re-key was recorded as a capture and the surrogate key stood here. The codegen defect is fixed and the re-key landed in Re-key the swap legs to the trade.
Verification: the component regenerates with no drift; the codegen suite is 1276 passed; the full preset build exits 0 with no errors and no warnings.
Not verified live: that an imported swap lands its leg rows. The database
and the service fleet are down in this environment. Proving it means
compass db recreate followed by a fleet start and an ORE import, which is
a deliberate, whole-environment step rather than something to do as a
side-effect of a build.