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).

Emacs 29.3 (Org mode 9.6.15)