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.

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.

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

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.

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.

Decisions

Out of scope

Emacs 29.3 (Org mode 9.6.15)