Architecture
The examples are clients of a Virtufin platform. They never talk to a service directly: everything goes through the API gateway, and the context decides which platform that is.
System Overview
flowchart LR
subgraph Client["Your machine"]
EX[Example<br/>notebook / script]
CTX[~/.config/virtufin/api/contexts.toml]
end
subgraph Platform["Virtufin platform (demo cluster or local)"]
API[API gateway]
WSM[WebSocketManager]
WM[WorkManager]
RT[Runtime<br/>workers]
STATE[(State store)]
PUBSUB[(Pub/sub<br/>topics)]
end
FEED[(Virtufin package feed<br/>NuGet / PyPI)]
EXCH[Binance / Alpaca]
EX -.->|reads context| CTX
EX -->|gRPC, TLS + x-api-key on demo| API
API --> WSM & WM & STATE & PUBSUB
WSM <-->|wss://| EXCH
WSM -->|publish frames| PUBSUB
WM -->|load worker packages| FEED
WM --> RT
PUBSUB <-->|subscribe / publish| RT
EX -.->|publish test workers| FEED
Choosing the platform: contexts
Every example names its platform with a context constant near its top, "demo" by default. The name is looked
up in ~/.config/virtufin/api/contexts.toml (Getting Started):
| Context | Gateway | Notes |
|---|---|---|
demo |
api.virtufin.com:443, TLS, x-api-key header |
The shared cluster. Examples remove what they create |
local |
localhost:5002, plaintext |
A backend you run yourself (Deployment) |
All three languages use the gateway's native gRPC port. A context the file doesn't have falls back to
localhost:5002.
Services Used
| Service | Role in the examples |
|---|---|
| API gateway | The only entry point: State, pub/sub, and calls to the other services (websocketmanager.Connect, workmanager.CreateWorker, ...) |
| WebSocketManager | Holds the WebSocket connections to Binance and Alpaca; forwards subscribe/auth messages (SendRaw) and publishes every frame to a topic (StartPublish) |
| WorkManager | Creates, starts, stops and deletes workers; loads each worker's package from the feed into the Runtime |
| Runtime | Runs the workers. A worker subscribes to topics, processes CloudEvents and publishes its results; every worker on a topic gets every message |
| State | Key/value store per registered service (statestore, websocketmanager, workmanager); the gateway rejects other service names |
| Pub/sub | Topics carrying CloudEvents with Virtufin's envelope extensions; a subscription starts at the topic's tail |
Workers
The engine workers the notebooks deploy (BinanceCandles, SimpleMovingAverage, LogReturn,
ShannonEntropy, MovingAverage, SimulatedExecutor, ...) are packages on the Virtufin feed, published from
the engine repos. The devkit notebooks write their own worker in dotnet/workers/, publish it to the feed under
a throwaway version, deploy it next to the engines, and delete it again in Cleanup.
Every topic and state key comes from the naming library
(lib.docs.virtufin.com/naming); the notebooks build theirs
through dotnet/names.cs.
Pairs are canonical (BTC_USDT); Binance's own symbol (BTCUSDT) appears only where an example talks
to Binance. Names follow the envelope's scoping:
| Scope | Topic | Example | State key |
|---|---|---|---|
| World (market data) | act.exchange.<venue>.<entity>.<event>[.<PAIR>…] |
act.exchange.binance.candle.closed.BTC_USDT.1d |
act.exchange.binance.orderbook.BTC_USDT |
| Scenario (indicators) | sc.<scenario>.indicator.<name>.changed.<PAIR> |
sc.PIPELINE.indicator.sma7.changed.BTC_USDT |
sc.PIPELINE.indicator.sma7.BTC_USDT |
| Scenario (trading) | sc.<scenario>.trading.order.<event> |
sc.DEMO.trading.order.filled |
— |
| Worker commands | wrk.<type>.command.<worker_id> |
wrk.httpgetworker.command.{worker_id} |
— |
| Example-only | x.<segments> |
x.notebook.events |
x.notebook.demo |
The Python and TypeScript scripts use their own x.* names (see Data Flow), outside
every published contract.