Skip to content

Getting started

The shortest path through the Aetelier SDK. By the end of this page you will have a Rust project that connects to Bybit, streams a few minutes of live order-book and trade data, and writes a small Parquet file you can read back in any analytics tool.

If you want the full runnable variant with logging, error handling, and a TOML manifest, jump to Tutorial 1: Bybit → Parquet.

Prerequisites

  • Rust 1.85+ (the workspace uses edition 2024).
  • A Bybit account is not required — we'll use the public WebSocket endpoint, which doesn't need authentication for market data.

1. Create a new crate

cargo new --bin atelier-quickstart
cd atelier-quickstart

2. Add atelier-sdk to Cargo.toml

[package]
name = "atelier-quickstart"
version = "0.1.0"
edition = "2024"

[dependencies]
atelier-sdk = { version = "0.0.10", features = ["parquet"] }
tokio       = { version = "1", features = ["full"] }
anyhow      = "1"
tracing-subscriber = "0.3"

atelier-sdk is a facade crate — adding it pulls in the workspace crates you'll touch (atelier-types, atelier-connect, atelier-io, etc.) so you don't have to list each one.

The parquet feature opts into the Parquet sink and the Parquet reader/writer functions in atelier-io.

3. The minimum viable worker

use atelier_sdk::atelier_connect::{
    clients::bybit::BybitWssClient,
    workers::{MarketWorker, MarketWorkerConfig},
};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    // 1. Build a Bybit client subscribed to BTCUSDT order book + trades.
    let client = BybitWssClient::builder()
        .subscribe_orderbook("BTCUSDT", 1)
        .subscribe_trades("BTCUSDT")
        .build()?;

    // 2. Run the connection in a background task.
    tokio::spawn(async move {
        if let Err(e) = client.run().await {
            eprintln!("client error: {e}");
        }
    });

    // 3. Configure a MarketWorker that emits a snapshot every 100 ms.
    let (tx, mut rx) = mpsc::channel(1024);
    let config = MarketWorkerConfig::default()
        .with_interval_ms(100);
    let mut worker = MarketWorker::new(config, tx);

    // 4. Drain snapshots until you stop the program.
    while let Some(snapshot) = rx.recv().await {
        println!(
            "{}  {} levels  {} trades",
            snapshot.timestamp,
            snapshot.orderbook.len(),
            snapshot.trades.len(),
        );
    }

    Ok(())
}

Run it:

cargo run

You should see one line per 100 ms with a level count and a trade count. Press Ctrl-C to stop.

4. Persist to Parquet

Replace the while let block with a Parquet sink. atelier-connect ships OutputSinkSet so you can fan out to multiple destinations at once — channel + Parquet is a common pattern for "give me snapshots in process AND on disk":

use atelier_sdk::atelier_connect::sinks::{OutputSinkSet, ParquetSink};

let parquet = ParquetSink::new("./data/snapshots")?;
let sinks   = OutputSinkSet::new()
    .with_parquet(parquet)
    .with_channel(tx);

Wire sinks into the worker instead of just tx. After running for a few minutes you'll have a tree like:

data/snapshots/
├── orderbooks/BTCUSDT_ob_sync_20260430_120000.000.parquet
└── trades/BTCUSDT_trades_sync_20260430_120000.000.parquet

The filename convention ({symbol}_{datatype}_{mode}_{ts}) is documented on the atelier-io page.

5. Read it back

use atelier_sdk::atelier_io::parquet::load_trades_from_parquet;

let trades = load_trades_from_parquet(
    "data/snapshots/trades/BTCUSDT_trades_sync_20260430_120000.000.parquet",
    None,  // load all columns
)?;

for trade in trades.iter().take(10) {
    println!("{}  {} {} @ {}",
        trade.timestamp, trade.side, trade.quantity, trade.price);
}

Drop Some(vec!["price".into(), "quantity".into()]) in for None to project only those columns.

What you just learned

Concept Where it lives
Exchange clients with reconnect atelier-connect
Workers that synchronize feeds atelier-connect MarketWorker
Output sinks (channel, terminal, parquet) atelier-connect OutputSinkSet
Parquet readers / writers atelier-io
The data types this all flows through atelier-types

Where to go next