Skip to content

Market Data — Quick Start

Get feedd running and consume your first stream.

Install

  • feedd binary
  • metad binary
  • Venue API credentials for at least one venue
  • Shared memory enabled on the host (/dev/shm on Linux)

See Install + Run for production-oriented installation.

Configure

feedd and metad use independent config files. Minimal example:

# /etc/sorcery/metad.toml
stack = "master"
# /etc/sorcery/feedd.toml
stack = "master"

[control_plane]
bind_host = "127.0.0.1"
bind_port = 5510

[venues.binance]
api_key_env    = "BINANCE_API_KEY"
api_secret_env = "BINANCE_API_SECRET"

# Initial instrument set. You can add/remove at runtime via control plane.
subscriptions = [
  "perp.binance:BTCUSDT",
]

Credential note: keep API key/secret in environment variables and reference their names in TOML. Control-plane note: bind_host / bind_port define where clients send UDP subscribe/snapshot commands.

Ring names are derived from stack:

  • Market data ring: /sorcery-{stack}-md
  • Metadata region: /sorcery-{stack}-metadata
  • Snapshot region: /sorcery-{stack}-snapshot

Run

# Start metadata first
metad --config /etc/sorcery/metad.toml &

# Export venue credentials for feedd
export BINANCE_API_KEY="..."
export BINANCE_API_SECRET="..."

# Start market data adapter
feedd --config /etc/sorcery/feedd.toml

Subscribe to an instrument

If you configured subscriptions in feedd.toml, streaming starts on boot.

For runtime changes, use your deployment's Control Plane client to add/remove instruments. See Integration Guide and Ordering + Sequencing for recovery semantics when subscriptions change or connections reset.

Consume from SHM

#include <sorcery/types.h>
#include <sorcery/ring.h>
#include <sorcery/metadata.h>

auto ring = sorcery::Ring::open("/sorcery-master-md");
sorcery::Consumer consumer{ring};
sorcery::DrainBuffer drain;

sorcery::MetadataStore meta("/sorcery-master-metadata");

while (running) {
    for (auto& frame : consumer.drain(drain, 256)) {
        if (frame.is_gap()) {
            // Invalidate local books and request snapshot recovery.
            continue;
        }

        auto* hdr = frame.header();
        auto* body = frame.body();

        switch (hdr->msg_type) {
            case sorcery::STATUS: {
                auto* st = reinterpret_cast<const sorcery::StatusMsg*>(body);
                printf("venue=%u conn=%u active=%u\n",
                       hdr->venue, st->conn_state, st->active_instruments);
                break;
            }
            case sorcery::L1: {
                auto* l1 = reinterpret_cast<const sorcery::L1Msg*>(body);
                auto* inst = meta.find_instrument(hdr->inst_id);
                if (!inst) break;
                double bid = inst->to_price(l1->bid_px);
                double ask = inst->to_price(l1->ask_px);
                printf("inst=%lu bid=%.2f ask=%.2f\n", hdr->inst_id, bid, ask);
                break;
            }
            default:
                break;
        }
    }
}

Verify health

Check for periodic STATUS messages (default once per second per venue):

  • conn_state = 1 (CONNECTED)
  • last_rx_age_ns stays bounded
  • active_instruments > 0 for subscribed venues

On any ring gap, follow Ordering + Sequencing before trusting local book state again.

Stop

Send SIGTERM and wait for clean shutdown:

pkill -TERM feedd
pkill -TERM metad

For production process control, prefer systemd or container orchestration. See Install + Run.