Skip to content

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.