Task: Add the equity instrument verb units
Table of Contents
This page documents a task in the Manage trading entities from the shell story. It captures the goal, current status, acceptance, and any notes or results.
1. Goal
ores.shell extends the trading command folder to the nine equity instrument families, cloned from the fx-batch skeleton. Each unit (equity_accumulator, equity_asian_option, equity_barrier_option, equity_digital_option, equity_forward, equity_option, equity_position, equity_swap, equity_variance_swap) declares the entity-specific data: trading.v1 subject family, request and response classes from the per-product protocol, save columns from the org model, instrument_id key. The batch runs the live equity system test: every unit round-trips add, list, history and delete through the shell against the fleet, with each save verified at the DB layer and on the changed-event stream. Any quirk a family exposes (position versus instrument semantics, save columns the fx convention does not cover) is resolved here and distilled back into the shared skeleton.
2. Status
| Field | Value |
|---|---|
| State | DONE |
| Parent story | Manage trading entities from the shell |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Rates batch (task_shell-trading-rates-instrument-verbs). |
| Last touched | 2026-09-07 |
3. Acceptance
- The nine equity units exist under projects/ores.shell/src/app/commands/trading/ and compile; trading_commands registers them; the shell cmake component files regenerate with no drift.
- Live against the fleet: add, list and page, history and delete round-trip every equity family through its trading.v1.equity_* subjects; each save lands a DB row (verified via psql) and emits the changed event.
- Full preset build and ctest green; shell cmake-sources drift clean. The batch lands on feature/shell-trading-instrument-verbs as one [ores.shell] commit.
4. Plan
The nine equity units clone the fx-batch generator (a python
string.Template over a PRODUCTS table, /tmp/fxgen/gen_equity_units.py)
and differ only in declared data: trading.v1. subject family,
request and response classes from the per-product protocol, save
columns from the org model, instrument_id key. The equity families
exercise the nullable-column paths the fx set does not: per-family
CHECK constraints gate a column pair — accumulator and digital option
demand NULL on the counterpart of a NOT NULL code-specific column,
option, position and swap expose the inverse. The cli splits
arguments with RemoveEmptyEntries, so no quoting spelling can carry
an empty positional token; the not-set literal for the shell is a
single -, translated at the two choke points: parse_optional_double
maps - to nullopt and the estr save columns map - to the empty
string, which the persistence mappers store as NULL.
Live-test protocol per family: scripted shell run as
tenant_admin@acme adds with - on the NULL-demanding columns, gets
and pages, histories, deletes; each add verified at the DB layer
(psql row under the acme tenant with the CHECK-gated pair exactly
NULL where demanded) and on the changed-event stream, with the
subscriber on the prefixed subject ores.dev.brave_hopper.ores.trading.>.
5. Notes
6. Test Scenarios
Manual QA scenarios (scaffolded via compass add test_scenario, run
through the QA Validation Runner panel) 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 |
|---|---|---|
7. PRs
| PR | Title |
|---|---|
8. Review
| Comment summary | File | Decision | Notes |
|---|---|---|---|
9. Result
Shipped the nine equity instrument verb units under
projects/ores.shell/src/app/commands/trading/
(equity_accumulator_instrument_commands.{hpp,cpp} through
equity_variance_swap_instrument_commands.{hpp,cpp}), each cloning
the fx-batch skeleton with its own trading.v1 subject family and
positional add arguments from the org columns. trading_commands.cpp
registers the nine plural menus; component_files.cmake lists the
units. Each modeling org gained a Table display section and its
*_table.cpp prints the fixed column set. The batch is one
[ores.shell] commit, fb897f374b, on
feature/shell-trading-instrument-verbs.
Live acceptance met on brave_hopper against the fleet. A scripted run
per family logged in as tenant_admin@acme round-tripped add, get,
history and delete for all nine families. Every add landed a DB row
under the acme tenant (12baaab3-55f1-47fb-a409-2afde4ac42b9) with
modified_by = tenant_admin and emitted the product changed event;
delete soft-closed the row (bitemporal valid_to stamp) and emitted a
second changed event. Changed events were captured on the prefixed
subject ores.dev.brave_hopper.ores.trading.> (the first subscriber
attempt used the bare relative event name and caught nothing — the
wire subject always carries the ores.{tier}.{instance} prefix) for
a probe add and both probe deletes, proving add-to-NATs and
delete-to-NATs.
The - not-set literal carried the CHECK-gated NULLs end to end:
the EquityAccumulator add landed target_amount and target_type NULL
beside knock_out_level 95, the EquityDigitalOption add landed
option_type Call and strike 1.25 with barrier_level and barrier_type
NULL, and the EquityOption, EquityPosition and EquitySwap adds landed
their gated columns NULL (cliquet_frequency, option_data_json, and
basket_json with underlying ACME). Per-family psql checks confirmed
every conditional NULL exactly as the CHECKs demand. A closing
ddl-user sweep found all nine probe rows soft-closed out of the live
view and the leftover fx probe row removed.
Status at close: Batch committed (fb897f374b); full preset build and ctest green (one ores.database reconnect flake under a concurrent suite, green on re-run).