Bridge
StrategyWorkerBase<TS, TM, TP, TA> is the bridge between the pure
IDecide coalgebra and the WorkManager's worker contract
(IWorker.ProcessAsync(CloudEvent)).
Topic binding
A strategy needs both market and position events. Bind with two
explicit topics at CreateWorker (CreateWorkerRequest.topics; see
virtufin-workmanager's multi-topic worker support) -- e.g. the
scenario's market-data feed and its position feed. Do not bind with
a wildcard: the deployed Dapr pubsub component is pubsub.redis
(Redis Streams), which has no wildcard subscription support -- a
worker bound to sc.<scenarioId>.> (a pattern this doc used to
recommend) subscribes to a Redis Stream literally named
"sc.<scenarioId>.>", which nothing is ever published to. Both topics
arrive on the one worker and are separated by CloudEvent type.
Dispatch
flowchart TD
E[CloudEvent] --> T{type?}
T -->|virtufin.position.*| POS[PortfolioAlgebra.Apply<br/>fold holdings]
POS --> N1[no response]
T -->|virtufin.market.*| M{is TMarket?}
M -->|no| N2[skipped]
M -->|yes| STEP["Decide.Step(state, (market, portfolio))"]
STEP --> ENC["ToOrderSubmission"]
ENC --> RESP["Order submitted CloudEvent"]
T -->|other| N3[ignored]
- Position events are the feedback edge: fills fold into the portfolio, so the next decision sees updated holdings. No response is emitted.
- Market events step the strategy; an emitted action becomes the
pubsub-topics spec's "Order submitted" event (see
ToOrderSubmissionandBuildEnvelope). At most one action per step is supported today --WorkerBase.IWorker.ProcessAsyncreturns exactly one response CloudEvent per trigger. TMarketfiltering: choosingRichMarketEventasTMarketaccepts derived signals (candles, VWAP, volatility) but skips rawMarketEventticks — a deliberate type-level filter.
Response routing
Unlike a typical WorkerBase worker (which publishes on output.Type,
overridable per message via replytopic), the trading envelope keeps
ce-type fixed at com.virtufin.trading.order.submitted regardless of
replytopic -- it's a stable identifier per the pubsub-topics spec,
independent of the per-scenario topic. Routing instead goes through
WorkerBase.PublishTopicAttribute (publishtopic), set by
BuildEnvelope to sc.<scenarioid>.trading.order.submitted; the
WorkManager reads that extension to pick the publish topic and strips
it before publish, so subscribers never see it (see
virtufin-workmanager's WorkManager.PublishOutputAsync).
State
Strategy state and portfolio state live in worker instance fields — the engine keeps one worker object alive for the instance's lifetime, so state threads across deliveries. State is the driver's accumulator, never part of the input pair (see the behaviour spec's "State is not part of the input pair" requirement).
Config
Config is extension-only. ConfigResolution reads CloudEvent extension
attributes (where the WorkManager stamps CreateWorkerRequest.config);
payload fields are never consulted as config.