Skip to content

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 trade response.
  • TMarket filtering: choosing RichMarketEvent as TMarket accepts derived signals (candles, VWAP, volatility) but skips raw MarketEvent ticks — 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.