Story: Move trade import orchestration to service layer

Table of Contents

This page documents a story in Sprint 22. It captures the goal, current status, acceptance criteria, and the tasks that compose it.

1. Goal

Trade import today diverges from market data import's architecture: import_market_data_request (ores.marketdata.core::service::import_service) is fully server-side — the client just forwards raw file bytes — but trade import's ~300 lines of orchestration (portfolio XML parsing via ores.ore.core::xml::importer, per-trade save_trade_request, and a 30-way per-instrument-type save_<type>_instrument_request dispatch) live entirely inside projects/ores.qt/trading/src/ImportTradeDialog.cpp (lines ~744-1045), with no service-layer backing. Move that orchestration into a new ores.trading.core::service::trade_import_service, backed by a new import_trades_request=/=import_trades_response NATS message pair mirroring import_market_data_request. This lets Qt shrink back to progress/cancel UI only, and lets ores.shell command for market data import's planned trade import shell command be a thin wrapper around the same service instead of duplicating the 30-way instrument dispatch a second time.

This story is an enabler with no direct blocker relationship to Synthetic data collections: Basic and Realistic, but both it and the sibling shell story exist to give that story's imports a non-interactive path.

2. Status

Field Value
State BACKLOG
Parent sprint Sprint 22
Now Not yet started.
Waiting on Nothing.
Next Break the story into tasks.
Last touched 2026-07-04

3. Acceptance

  • import_trades_request=/=import_trades_response exist in ores.trading.api, and ores.trading.core::service::trade_import_service parses portfolio XML, saves trades, and dispatches per-instrument-type saves server-side, registered as a NATS handler.
  • ImportTradeDialog sends a single import_trades_request instead of building 31 requests inline; only progress/cancel UI remains in the client.
  • ores.shell gains a trade import --file <portfolio.xml> command that is a thin wrapper around the same request.

4. Tasks

Task State Start End Description
Add import_trades_request/response protocol and trade_import_service BACKLOG     Define import_trades_request/import_trades_response in ores.trading.api (mirroring import_market_data_request's shape: raw portfolio XML content in, trade_count/instrument_count/error info out), implement ores.trading.core::service::trade_import_service parsing via ores.ore.core::xml::importer, saving each trade and dispatching per-instrument-type saves (the ~300 lines currently in ImportTradeDialog.cpp:744-1045), and register the NATS handler mirroring ores.marketdata.core's import_service registration.
Refactor ImportTradeDialog to use trade_import_service BACKLOG     Replace ImportTradeDialog's inline per-trade/per-instrument-type request loop (~744-1045) with a single call to the new import_trades_request, keeping only the ~75 lines of legitimate UI concerns (progress updates, liveness guard, cancellation handling).
Add ores.shell trade import command BACKLOG     Add a 'trade import –file <portfolio.xml path>' command to ores.shell (mirroring the marketdata_commands pattern), sending import_trades_request and printing trade_count/instrument_count/errors. Blocked on 'Add import_trades_request/response protocol and trade_import_service'; ideally also after the Qt dialog refactor, though both consume the same server API so order between them is flexible.

5. Decisions

6. Out of scope

Emacs 29.3 (Org mode 9.6.15)