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 worker binds to exactly one topic at CreateWorker. A strategy needs
both market and position events, so bind with a NATS wildcard:
sc.<scenarioId>.>
Both streams arrive on one subscription 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["enrich + encode actions"]
ENC --> RESP[response 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; if the strategy emits actions
they become a
traderesponse. TMarketfiltering: choosingRichMarketEventasTMarketaccepts derived signals (candles, VWAP, volatility) but skips rawMarketEventticks — a deliberate type-level filter.
Response routing
The WorkManager publishes the response on output.Type. The bridge
uses the worker's default response type, overridable per message via the
replytopic extension (set by publish_with_result callers or by a
fixed CreateWorkerRequest.config entry).
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.