sequence-algo-sdk 0.3.2

Sequence Markets Algo SDK — write HFT trading algos in Rust, compile to WASM, deploy to Sequence
Documentation
  • Coverage
  • 64.25%
    230 out of 358 items documented0 out of 154 items with examples
  • Size
  • Source code size: 152.69 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 10.98 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 14s Average build duration of successful builds.
  • all releases: 14s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • Bai-Funds/algo-sdk
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • muhammad-awan0

sequence-algo-sdk

Crates.io docs.rs License

Write ultra-low-latency trading algos in Rust, compile to WASM, deploy to Sequence Markets. Zero dependencies. The SDK provides two algo traits — single-venue and multi-venue — along with order book types, action buffers, and everything you need to build trading algorithms that run on Sequence's edge infrastructure.

Quick Start

# Cargo.toml
[package]
name = "my-algo"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]
sequence-algo-sdk = { version = "0.3", default-features = false }

[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
panic = "abort"

Single-Venue Algo

Receives one venue's order book. Good for market making, momentum, mean reversion on a single exchange.

// src/lib.rs
#![no_std]
extern crate alloc;
use algo_sdk::*;

struct MyAlgo {
    next_id: u64,
}

impl Algo for MyAlgo {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, actions: &mut Actions) {
        // Your trading logic here
        if book.spread_bps() > 10 && state.is_flat() {
            self.next_id += 1;
            actions.buy(self.next_id, 1_000_000, book.bids[0].px_1e9 + 100);
        }
    }

    fn on_fill(&mut self, _fill: &Fill, _state: &AlgoState) {}
    fn on_reject(&mut self, _reject: &Reject) {}
    fn on_shutdown(&mut self, _state: &AlgoState, actions: &mut Actions) {
        actions.clear(); // Cancel all pending orders
    }
}

export_algo!(MyAlgo { next_id: 0 });

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }

Multi-Venue Algo

Receives an NbboSnapshot with the best bid/ask across all connected venues (up to 16 CEX + DEX), plus VenueBooks with full 20-level depth per venue. Use it for cross-venue arbitrage, multi-venue market making, or best-execution strategies.

// src/lib.rs
#![no_std]
extern crate alloc;
use algo_sdk::*;

struct ArbAlgo {
    next_id: u64,
}

impl MultiVenueAlgo for ArbAlgo {
    fn on_nbbo(
        &mut self,
        nbbo: &NbboSnapshot,
        books: &VenueBooks,
        state: &AlgoState,
        actions: &mut Actions,
    ) {
        // Need at least 2 venues and a crossed market
        if nbbo.venue_ct < 2 || !nbbo.is_crossed() {
            return;
        }

        // Skip stale venues (>3 seconds old)
        let bid_v = nbbo.nbbo_bid_venue as usize;
        let ask_v = nbbo.nbbo_ask_venue as usize;
        if nbbo.is_venue_stale(bid_v, 3000) || nbbo.is_venue_stale(ask_v, 3000) {
            return;
        }

        // Access per-venue depth for smarter sizing
        if let Some(kraken_book) = books.book_for_venue(VENUE_KRAKEN) {
            // Use full depth to size orders appropriately
            let _depth_at_best = kraken_book.bids[0].sz_1e8;
        }

        // Route orders to specific venues
        let bid_venue_id = nbbo.venue_ids[bid_v];
        let ask_venue_id = nbbo.venue_ids[ask_v];

        if state.is_flat() {
            self.next_id += 1;
            actions.ioc_buy_on(ask_venue_id, self.next_id, 1_000_000, nbbo.nbbo_ask_px_1e9);
            self.next_id += 1;
            actions.ioc_sell_on(bid_venue_id, self.next_id, 1_000_000, nbbo.nbbo_bid_px_1e9);
        }
    }

    fn on_fill(&mut self, _fill: &Fill, _state: &AlgoState) {}
    fn on_reject(&mut self, _reject: &Reject) {}
    fn on_shutdown(&mut self, _state: &AlgoState, actions: &mut Actions) {
        actions.clear();
    }
}

export_multi_venue_algo!(ArbAlgo { next_id: 0 });

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }

Install the CLI

macOS / Linux

curl -fsSL https://raw.githubusercontent.com/Bai-Funds/algo-sdk/main/install.sh | sh

This detects your platform, downloads the latest sequence binary, verifies the SHA-256 checksum, and installs to ~/.local/bin.

Windows (PowerShell)

irm https://raw.githubusercontent.com/Bai-Funds/algo-sdk/main/install.ps1 | iex

Installs to ~/.sequence/bin. You may need to add this directory to your PATH.

Windows (Git Bash / WSL)

The shell installer also works in Git Bash, MSYS2, and WSL:

curl -fsSL https://raw.githubusercontent.com/Bai-Funds/algo-sdk/main/install.sh | sh

Manual Download

Download the archive for your platform from GitHub Releases (look for tags starting with cli/v):

Platform Archive
macOS (Apple Silicon) sequence-aarch64-apple-darwin.tar.gz
macOS (Intel) sequence-x86_64-apple-darwin.tar.gz
Linux (x86_64) sequence-x86_64-unknown-linux-gnu.tar.gz
Linux (ARM64) sequence-aarch64-unknown-linux-gnu.tar.gz
Windows (x86_64) sequence-x86_64-pc-windows-msvc.zip

Extract and place sequence (or sequence.exe on Windows) somewhere on your PATH.

Verify Installation

sequence --version

Update to Latest Version

sequence update

This checks GitHub Releases for a newer cli/v* tag, downloads it, verifies the checksum, and replaces the current binary in-place. Works on macOS, Linux, and Windows.

Build & Deploy

# Single-venue algo
sequence init my-algo
cd my-algo
sequence build
sequence deploy BTC-USD --start
sequence logs BTC-USD --follow

# Multi-venue algo
sequence init my-arb --multi-venue
cd my-arb
sequence build
sequence deploy ETH-USDC --start

Or build manually:

cargo build --target wasm32-unknown-unknown --release

Paper Trading (Sandbox)

Test your algo against live market data without placing real orders:

# Log in with a sandbox key (seq_test_...)
sequence login --sandbox

# Deploy and run in sandbox mode
sequence --sandbox deploy BTC-USD --start
sequence --sandbox logs BTC-USD --follow
sequence --sandbox list
sequence --sandbox stop BTC-USD

Sandbox and live deployments are fully isolated — same symbol, same client, different infrastructure. You can run both simultaneously. For CI/CD, set SEQUENCE_SANDBOX_API_KEY instead of using --sandbox.

Key Types

Core

Type Description
Algo Single-venue trait — on_book, on_fill, on_reject, on_shutdown
MultiVenueAlgo Multi-venue trait — on_nbbo, on_fill, on_reject, on_shutdown
L2Book 20-level order book (656 bytes, fits in L1 cache)
AlgoState Position, open orders, session PnL, symbol metadata, and risk limits
Actions Buffer for placing/canceling orders (up to 16 per tick)
Fill Fill event with price, quantity, and timing
Reject Rejection with typed error codes

Multi-Venue

Type Description
NbboSnapshot Cross-venue NBBO with per-venue BBO, staleness tracking, up to 16 venues
VenueBooks Merged + per-venue 20-level depth books (~11KB)
PoolBooks Per-pool depth from DEX liquidity pools (~23KB, up to 32 pools)
PoolMeta Pool identity: address, chain, protocol, fee tier (40 bytes)
MAX_VENUES Maximum venues in an NbboSnapshot (16)
MAX_POOLS Maximum individual pool slots in a PoolBooks (32)

Venue Reference

Every venue has a constant ID you can use for filtering, order routing, and depth lookups.

CEX Venues

Constant Value Name Description
VENUE_KRAKEN 1 "kraken" Kraken exchange
VENUE_COINBASE 2 "coinbase" Coinbase exchange
VENUE_BINANCE 3 "binance" Binance exchange
VENUE_BITGET 4 "bitget" Bitget exchange
VENUE_CRYPTOCOM 5 "cryptocom" Crypto.com exchange
VENUE_BITMART 6 "bitmart" BitMart exchange
VENUE_OKX 8 "okx" OKX exchange
VENUE_BYBIT 9 "bybit" Bybit exchange
VENUE_UNKNOWN 10 "unknown" Unknown/unrecognized venue

DEX Venues

Constant Value Name Chain Supported Protocols
VENUE_DEX 7 "dex" Aggregated All chains combined
VENUE_DEX_ETH 11 "dex-eth" Ethereum Uniswap V2/V3, Curve, Balancer V2
VENUE_DEX_ARB 12 "dex-arb" Arbitrum Uniswap V3, Camelot (Algebra V3)
VENUE_DEX_BASE 13 "dex-base" Base Uniswap V3, Aerodrome (Solidly)
VENUE_DEX_OP 14 "dex-op" Optimism Uniswap V3, Velodrome (Solidly)
VENUE_DEX_POLY 15 "dex-poly" Polygon Uniswap V2, Balancer V2, Curve
VENUE_DEX_SOL 16 "dex-sol" Solana Raydium CLMM, Orca Whirlpool (via Jupiter)

Note: Value 10 is reserved. Per-chain DEX venues (11-16) provide chain-specific depth, while VENUE_DEX (7) aggregates across all chains.

Venue Helper Functions

// Classification
is_dex(VENUE_DEX_ARB)    // true  — any DEX venue (7, 11-16)
is_dex(VENUE_DEX_SOL)    // true  — Solana is a DEX venue
is_cex(VENUE_KRAKEN)     // true  — any CEX venue (1-6, 8-9)
is_dex(VENUE_COINBASE)   // false

// Name lookup
venue_name(VENUE_KRAKEN)   // "kraken"
venue_name(VENUE_DEX_ARB)  // "dex-arb"
venue_name(VENUE_DEX_SOL)  // "dex-sol"
venue_name(0)              // "unknown"

Filtering by Venue Type

fn on_nbbo(&mut self, nbbo: &NbboSnapshot, books: &VenueBooks, state: &AlgoState, actions: &mut Actions) {
    // Iterate only CEX venues
    for slot in 0..books.book_ct as usize {
        let vid = books.venue_id_at(slot);
        if is_cex(vid) {
            let book = books.book_at_slot(slot);
            // ... CEX-specific logic
        }
    }

    // Count venue types
    let cex_count = books.cex_count();
    let dex_count = books.dex_count();

    // Direct venue lookup
    if let Some(binance_book) = books.book_for_venue(VENUE_BINANCE) {
        // ... use Binance depth
    }
}

VenueBooks

VenueBooks provides full 20-level depth from every connected venue, plus a merged cross-venue book. Available in on_nbbo() for multi-venue algos.

Fields

Field Type Description
merged L2Book Cross-venue merged depth (same-price levels aggregated)
book_ct u8 Number of venues with depth data
venue_ids [u8; 16] VenueId per slot (same order as NbboSnapshot)
books [L2Book; 16] Per-venue L2Book per slot

Methods

// Look up a specific venue's book by VenueId constant
books.book_for_venue(VENUE_KRAKEN)   -> Option<&L2Book>

// Direct slot access (faster, no scan)
books.book_at_slot(0)                -> &L2Book
books.venue_id_at(0)                 -> u8

// Check if a venue has depth data
books.has_depth_for(VENUE_DEX_ARB)   -> bool

// Count by venue type
books.cex_count()                    -> usize
books.dex_count()                    -> usize

Using the Merged Book

The merged field aggregates depth from all venues. Same-price levels from different venues have their sizes summed. Levels are sorted (bids descending, asks ascending) and truncated to 20 per side.

// Use merged book for reference pricing
let mid = books.merged.mid_px_1e9();
let spread = books.merged.spread_bps();

// Total visible liquidity across all venues
let total_bid_depth: u64 = (0..books.merged.bid_ct as usize)
    .map(|i| books.merged.bids[i].sz_1e8)
    .sum();

PoolBooks

PoolBooks gives per-pool granularity for DEX liquidity. While VenueBooks gives per-chain depth (aggregated across all pools on a chain), PoolBooks breaks it down to individual Uniswap V3, Curve, Raydium, etc. pools. Available at WASM offset 0x13000, read on-demand during on_nbbo().

Fields

Field Type Description
pool_ct u8 Number of active pool slots (0..32)
metas [PoolMeta; 32] Pool identity per slot
books [L2Book; 32] Per-pool L2Book per slot

PoolMeta

Field Type Description
address [u8; 32] Pool contract address (EVM: first 20 bytes, Solana: all 32)
pair_index u16 For multi-asset pools like Curve (0 for standard 2-token pools)
fee_bps u16 Pool fee in basis points (e.g., 5 = 0.05% for Uniswap V3)
venue_id u8 Chain identifier (VENUE_DEX_ARB, VENUE_DEX_SOL, etc.)
protocol_id u8 Protocol: 1=uniswap_v2, 2=uniswap_v3, 3=curve, 4=balancer_v2, 5=aerodrome, 6=velodrome, 7=camelot, 8=raydium_clmm, 9=orca_whirlpool

Methods

// Look up a pool by contract address and pair index
pool_books.book_for_pool(&address, pair_index)  -> Option<&L2Book>

// Direct slot access (faster, no scan)
pool_books.book_at_slot(0)   -> &L2Book
pool_books.meta_at_slot(0)   -> &PoolMeta

Example: Pool-Level Analysis

fn on_nbbo(&mut self, nbbo: &NbboSnapshot, books: &VenueBooks, state: &AlgoState, actions: &mut Actions) {
    // Read pool books from WASM memory
    let pool_books = unsafe { &*(algo_sdk::POOL_BOOKS_WASM_OFFSET as *const PoolBooks) };

    // Compare fee tiers across pools
    for i in 0..pool_books.pool_ct as usize {
        let meta = pool_books.meta_at_slot(i);
        let book = pool_books.book_at_slot(i);
        if meta.fee_bps <= 5 && book.spread_bps() < 10 {
            // Low-fee pool with tight spread — good for execution
        }
    }
}

NbboSnapshot

The NbboSnapshot is the core data structure for multi-venue algos. It contains:

  • Global NBBO: Best bid/ask price and size across all venues
  • Venue identification: Which venue has the best bid, which has the best ask
  • Per-venue BBO: Individual bid/ask for each connected venue (struct-of-arrays layout for cache efficiency)
  • Staleness tracking: Milliseconds since last update per venue — reject stale data before trading

Important index convention:

  • nbbo_bid_venue / nbbo_ask_venue are array slot indices (0..venue_ct-1), not VenueIds
  • Use venue_ids[nbbo_bid_venue] to get the actual VenueId for order routing
  • Action.venue_id takes the VenueId value, not the array slot

Helper methods:

  • nbbo_spread_bps() — spread in basis points (negative = crossed market)
  • is_crossed() — true when best bid > best ask (arb opportunity)
  • is_venue_stale(slot, max_ms) — true when venue data is older than max_ms
  • slot_for_venue(venue_id) — find the array slot for a given VenueId
  • best_bid_venue_id() — the VenueId (not slot) of the best bid venue
  • best_ask_venue_id() — the VenueId (not slot) of the best ask venue

Order Routing

Single-venue algos use actions.buy() / actions.sell() — orders go to the algo's assigned venue.

Multi-venue algos use venue-targeted methods:

actions.buy_on(venue_id, order_id, qty, price);     // Limit buy on specific venue
actions.sell_on(venue_id, order_id, qty, price);     // Limit sell on specific venue
actions.ioc_buy_on(venue_id, order_id, qty, price);  // IOC buy on specific venue
actions.ioc_sell_on(venue_id, order_id, qty, price); // IOC sell on specific venue

Set venue_id = 0 for default venue routing (same as single-venue behavior).

DEX Depth: How It Works

DEX order books are synthetic — constructed by probing AMM liquidity pools, not from a traditional order book. Understanding the methodology helps you use DEX depth data correctly.

Construction

DEX depth is built by simulating swaps at 16 discrete USD sizes:

$10, $25, $50, $100, $250, $500, $750, $1K, $2.5K, $5K, $10K, $25K, $50K, $100K, $250K, $500K

Each probe returns an effective price at that size, which is converted into a bid/ask level. The result is a 20-level L2Book that approximates the continuous AMM curve as discrete levels.

Gas Adjustment

All DEX prices are gas-adjusted: they include estimated swap gas cost at the current base fee. This means the prices you see already account for execution costs — a bid of $1000 on DEX means $1000 after gas.

Routing

  • Below $10K: Single best-pool routing (lowest-impact single pool)
  • Above $10K: Greedy multi-pool split to reduce price impact across multiple pools

Accuracy

DEX depth is validated every 10th emission cycle:

  • Suppressed if the $100 probe has >25bps error vs. on-chain simulation
  • Suppressed if the $10K probe has >75bps error
  • When suppressed, the last valid book is held (no stale data pushed)

Staleness

DEX data updates more slowly than CEX data. The venue_update_ms field in NbboSnapshot tracks freshness:

  • CEX staleness threshold: ~10% outlier tolerance
  • DEX staleness threshold: ~25% outlier tolerance (wider due to block times)

Use nbbo.is_venue_stale(slot, max_ms) to gate on freshness before trading DEX venues.

DEX vs CEX in Your Algo

fn on_nbbo(&mut self, nbbo: &NbboSnapshot, books: &VenueBooks, state: &AlgoState, actions: &mut Actions) {
    // Only trade CEX venues with tight spread
    for slot in 0..nbbo.venue_ct as usize {
        let vid = nbbo.venue_ids[slot];
        if is_cex(vid) && !nbbo.is_venue_stale(slot, 2000) {
            // CEX venue is fresh — safe to trade
        }
    }

    // Use DEX depth for reference/hedging but with wider staleness tolerance
    if let Some(dex_book) = books.book_for_venue(VENUE_DEX_ARB) {
        if books.has_depth_for(VENUE_DEX_ARB) {
            let dex_mid = dex_book.mid_px_1e9();
            // Compare DEX mid vs CEX mid for cross-venue signal
        }
    }
}

Fixed-Point Format

All prices and quantities use fixed-point integers for deterministic, allocation-free arithmetic:

  • Prices: px_1e9 — multiply by 10^9 (e.g., $50,000.00 = 50_000_000_000_000)
  • Quantities: qty_1e8 — multiply by 10^8 (e.g., 1.0 BTC = 100_000_000)

Examples

See examples/ for complete working algos:

  • mm-algo — Single-venue market maker with spread-based quoting
  • arb-algo — Multi-venue arbitrage with crossed-market detection and staleness gating
  • test-algo — Minimal algo for testing and validation
  • speed-test-algo — Performance benchmarking
  • xrp-5usd-mm — XRP-specific market maker

Backtesting

Build your algo to WASM and run it against recorded market data:

# Single symbol
sequence backtest BTC-USD --start 2026-03-01 --end 2026-03-08

# Multi-symbol portfolio
sequence backtest --symbols BTC-USD,ETH-USD,SOL-USD --start 2026-03-01 --end 2026-03-08

Or call sim-engine directly:

sim-engine run --wasm target/wasm32-unknown-unknown/release/my_algo.wasm \
  --symbol BTC-USD --start 2026-03-01 --end 2026-03-08 --capital 100000

Multi-Symbol Portfolio Mode

When using --symbols, events from all symbols are merged into a single timeline. Your algo receives L2Book updates with symbol_id set so you can distinguish which instrument updated:

fn on_book(&mut self, book: &L2Book, state: &AlgoState, actions: &mut Actions) {
    match book.symbol_id {
        1 => self.handle_btc(book, state, actions),
        2 => self.handle_eth(book, state, actions),
        _ => {}
    }
}

Symbol IDs are assigned in order: the first symbol in --symbols gets ID 1, the second gets ID 2, etc.

Fills and rejects also carry symbol context (v0.4+):

fn on_fill(&mut self, fill: &Fill, state: &AlgoState) {
    match fill.symbol_id() {
        1 => self.btc_position += fill.qty_1e8,
        2 => self.eth_position += fill.qty_1e8,
        _ => {}
    }
}

Note: In multi-symbol mode, AlgoState shows portfolio-level aggregates (total position, total PnL across all symbols). Your algo must track per-symbol state internally using symbol_id() on books and fills.

Realism Controls

The sim engine models market microstructure effects that can be enabled via environment variables. All are off by default for backward compatibility:

Feature Env Var What It Does
Adverse selection MAKER_ADVERSE_ENABLED=true Toxic trade detection — probabilistic fill skip + price penalty on maker fills
Vol-conditioned latency LATENCY_VOL_MU_SCALE=0.3 Latency increases during high-volatility periods
Queue cancel attrition QUEUE_CANCEL_DECAY_RATE=0.5 Queue ahead of you decays over time from cancellations
Self-impact SELF_IMPACT_ENABLED=true Your orders change the visible book depth

For MM backtests, enabling all features provides more realistic P&L estimates:

MAKER_ADVERSE_ENABLED=true SELF_IMPACT_ENABLED=true QUEUE_CANCEL_DECAY_RATE=0.5 \
  sequence backtest BTC-USD --start 2026-03-01 --end 2026-03-08

Calibration

Tune sim parameters against historical data:

sim-engine calibrate --symbol BTC-USD --venue kraken --target queue
sim-engine calibrate --symbol BTC-USD --venue kraken --target maker-adverse-fill-rate
sim-engine calibrate --symbol BTC-USD --venue kraken --target maker-adverse-penalty

Migrating from v0.2

v0.3 adds per-venue depth alongside the existing merged 20-level L2Book. Previously, multi-venue algos received a single merged L2Book aggregating all venues. Now VenueBooks gives access to both the merged view and individual venue order books.

v0.3 changes the MultiVenueAlgo trait signature:

// v0.2
fn on_nbbo(&mut self, nbbo: &NbboSnapshot, book: &L2Book, state: &AlgoState, actions: &mut Actions);

// v0.3
fn on_nbbo(&mut self, nbbo: &NbboSnapshot, books: &VenueBooks, state: &AlgoState, actions: &mut Actions);

The old merged L2Book is still available as books.merged. To migrate:

  1. Change book: &L2Book to books: &VenueBooks in your on_nbbo signature
  2. Replace any book. references with books.merged. (e.g., book.mid_px_1e9() becomes books.merged.mid_px_1e9())
  3. Optionally, start using per-venue depth via books.book_for_venue(VENUE_*) for smarter sizing

Pre-compiled v0.2 WASM binaries continue to work without recompilation.

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.