Sequence/docs

Getting Started

Make sure you've installed the CLI and configured your API key, then:

bash
sequence init my-algo
cd my-algo
# Edit src/lib.rs with your strategy
sequence build
sequence deploy BTC-USD --start

This scaffolds a project with sequence-algo-sdk from crates.io, the WASM target, and a starter Algo trait implementation.


Official Examples

These maintained examples are in the algo-sdk repo:

ExampleDescription
test-algoMinimal callback + logging sanity check
mm-algoReference market-making strategy
speed-test-algoLive round-trip and fill latency measurement
xrp-5usd-mmSmall-balance XRP/USD micro market maker (maker-first with controlled unwind)

Market Maker

Simple market maker that quotes both sides with inventory management.

rust
use algo_sdk::*;
 
struct MarketMaker {
    next_id: u64,
    max_position: i64,
    spread_bps: u32,
    order_size: i64,
}
 
impl Algo for MarketMaker {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        // Skip if spread too tight
        if book.spread_bps() < self.spread_bps {
            return;
        }
 
        // Cancel stale orders
        for i in 0..state.order_ct as usize {
            let order = &state.orders[i];
            if order.is_live() {
                actions.cancel(order.order_id);
            }
        }
 
        let mid = book.mid_px_1e9();
        let half_spread = mid * self.spread_bps as u64 / 20_000;
 
        // Calculate capacity
        let pos = state.position_1e8;
        let buy_room = self.max_position - pos;
        let sell_room = self.max_position + pos;
 
        // Place bid if room to buy
        if buy_room >= self.order_size {
            self.next_id += 1;
            actions.buy(self.next_id, self.order_size, mid - half_spread);
        }
 
        // Place ask if room to sell
        if sell_room >= self.order_size {
            self.next_id += 1;
            actions.sell(self.next_id, self.order_size, mid + half_spread);
        }
    }
 
    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.cancel_all(state);
    }
}
 
export_algo!(MarketMaker {
    next_id: 0,
    max_position: 100_000_000, // 1 unit
    spread_bps: 10,            // 10 bps
    order_size: 10_000_000,    // 0.1 units
});

Momentum

Trades in the direction of recent price movement.

rust
use algo_sdk::*;
 
struct Momentum {
    next_id: u64,
    last_mid: u64,
    threshold_bps: i64,
    order_size: i64,
    max_position: i64,
}
 
impl Algo for Momentum {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        let mid = book.mid_px_1e9();
        
        // Need price history
        if self.last_mid == 0 {
            self.last_mid = mid;
            return;
        }
 
        // Calculate momentum in bps
        let change_bps = ((mid as i64 - self.last_mid as i64) * 10_000) 
            / self.last_mid as i64;
        self.last_mid = mid;
 
        let pos = state.position_1e8;
 
        // Strong up move + can go long
        if change_bps > self.threshold_bps && pos < self.max_position {
            self.next_id += 1;
            actions.buy(self.next_id, self.order_size, book.asks[0].px_1e9);
        }
 
        // Strong down move + can go short
        if change_bps < -self.threshold_bps && pos > -self.max_position {
            self.next_id += 1;
            actions.sell(self.next_id, self.order_size, book.bids[0].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.cancel_all(state);
    }
}
 
export_algo!(Momentum {
    next_id: 0,
    last_mid: 0,
    threshold_bps: 5,           // 5 bps move triggers
    order_size: 50_000_000,     // 0.5 units
    max_position: 200_000_000,  // 2 units max
});

Mean Reversion

Fades moves when orderbook is imbalanced.

rust
use algo_sdk::*;
 
struct MeanReversion {
    next_id: u64,
    imbalance_threshold: i32,
    order_size: i64,
    max_position: i64,
}
 
impl Algo for MeanReversion {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        // Calculate order imbalance from top 5 levels
        let imb = book.imbalance_bps(5);
        let pos = state.position_1e8;
 
        // Strong bid imbalance (lots of bids) → price likely to drop → sell
        if imb > self.imbalance_threshold && pos > -self.max_position {
            self.next_id += 1;
            actions.sell(self.next_id, self.order_size, book.bids[0].px_1e9);
        }
 
        // Strong ask imbalance (lots of asks) → price likely to rise → buy
        if imb < -self.imbalance_threshold && pos < self.max_position {
            self.next_id += 1;
            actions.buy(self.next_id, self.order_size, book.asks[0].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.cancel_all(state);
    }
}
 
export_algo!(MeanReversion {
    next_id: 0,
    imbalance_threshold: 3000,  // 30% imbalance
    order_size: 20_000_000,     // 0.2 units
    max_position: 100_000_000,  // 1 unit max
});

Grid Trader

Places orders at fixed price intervals.

rust
use algo_sdk::*;
 
struct Grid {
    next_id: u64,
    levels: u8,
    spacing_bps: u64,
    size_per_level: i64,
}
 
impl Algo for Grid {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        // Cancel all existing
        for i in 0..state.order_ct as usize {
            if state.orders[i].is_live() {
                actions.cancel(state.orders[i].order_id);
            }
        }
 
        let mid = book.mid_px_1e9();
        let step = mid * self.spacing_bps / 10_000;
 
        // Place grid of bids below mid
        for i in 1..=self.levels {
            self.next_id += 1;
            let px = mid - step * i as u64;
            actions.buy(self.next_id, self.size_per_level, px);
        }
 
        // Place grid of asks above mid
        for i in 1..=self.levels {
            self.next_id += 1;
            let px = mid + step * i as u64;
            actions.sell(self.next_id, self.size_per_level, px);
        }
    }
 
    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.cancel_all(state);
    }
}
 
export_algo!(Grid {
    next_id: 0,
    levels: 5,                // 5 levels each side
    spacing_bps: 10,          // 10 bps apart
    size_per_level: 5_000_000, // 0.05 units each
});

TWAP

Time-weighted average price execution.

rust
use algo_sdk::*;
 
struct Twap {
    next_id: u64,
    target_qty: i64,
    slice_size: i64,
    filled: i64,
    side: i8,  // 1 = buy, -1 = sell
}
 
impl Algo for Twap {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        // Check if done
        if self.filled >= self.target_qty {
            return;
        }
 
        // Only place if no pending orders
        if state.live_order_count() > 0 {
            return;
        }
 
        let remaining = self.target_qty - self.filled;
        let this_slice = remaining.min(self.slice_size);
 
        self.next_id += 1;
        if self.side > 0 {
            // Join the bid
            actions.buy(self.next_id, this_slice, book.bids[0].px_1e9);
        } else {
            // Join the ask
            actions.sell(self.next_id, this_slice, book.asks[0].px_1e9);
        }
    }
 
    fn on_fill(&mut self, fill: &Fill, _state: &AlgoState) {
        self.filled += fill.qty_1e8;
    }
 
    fn on_reject(&mut self, _reject: &Reject) {}
    
    fn on_shutdown(&mut self, state: &AlgoState, actions: &mut Actions) {
        actions.cancel_all(state);
    }
}
 
export_algo!(Twap {
    next_id: 0,
    target_qty: 1_000_000_000,  // 10 units total
    slice_size: 10_000_000,     // 0.1 units per slice
    filled: 0,
    side: 1,  // Buying
});

Aggressive Taker

Takes liquidity using IOC orders when spread is favorable.

rust
use algo_sdk::*;
 
struct AggressiveTaker {
    next_id: u64,
    max_position: i64,
    order_size: i64,
    min_spread_bps: u32,
    last_trade_book: u64,
    cooldown_ticks: u64,
}
 
impl Algo for AggressiveTaker {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        // Cooldown between trades
        if book.recv_ns < self.last_trade_book + self.cooldown_ticks {
            return;
        }
        
        // Only trade when spread is wide enough
        if book.spread_bps() < self.min_spread_bps {
            return;
        }
        
        let pos = state.position_1e8;
        let imbalance = book.imbalance_bps(5);
        
        // Strong buy signal: ask-heavy book (likely to drop)
        if imbalance < -2000 && pos < self.max_position {
            self.next_id += 1;
            // IOC: take what's available, cancel rest
            actions.ioc_buy(self.next_id, self.order_size, book.asks[0].px_1e9);
            self.last_trade_book = book.recv_ns;
            log_info!("IOC BUY @ {}", book.asks[0].px_1e9);
        }
        
        // Strong sell signal: bid-heavy book (likely to rise then drop)
        if imbalance > 2000 && pos > -self.max_position {
            self.next_id += 1;
            actions.ioc_sell(self.next_id, self.order_size, book.bids[0].px_1e9);
            self.last_trade_book = book.recv_ns;
            log_info!("IOC SELL @ {}", book.bids[0].px_1e9);
        }
    }
 
    fn on_fill(&mut self, fill: &Fill, state: &AlgoState) {
        log_warn!("FILL: {} @ {} | pos={}", 
            fill.qty_1e8, fill.px_1e9, state.position_1e8);
    }
 
    fn on_reject(&mut self, reject: &Reject) {
        log_warn!("REJECT: {}", reject.reason());
    }
    
    fn on_shutdown(&mut self, state: &AlgoState, actions: &mut Actions) {
        actions.cancel_all(state);
        
        // Flatten with market orders
        if state.position_1e8 > 0 {
            self.next_id += 1;
            actions.market_sell(self.next_id, state.position_1e8);
        } else if state.position_1e8 < 0 {
            self.next_id += 1;
            actions.market_buy(self.next_id, -state.position_1e8);
        }
    }
}
 
export_algo!(AggressiveTaker {
    next_id: 0,
    max_position: 500_000_000,   // 5 units max
    order_size: 100_000_000,     // 1 unit per trade
    min_spread_bps: 8,           // Only trade with 8+ bps spread
    last_trade_book: 0,
    cooldown_ticks: 1_000_000_000, // 1 second cooldown
});

Sniper

Waits for a specific price level, then executes with FOK (all-or-nothing).

rust
use algo_sdk::*;
 
struct Sniper {
    next_id: u64,
    target_price: u64,  // Price to trigger at
    target_qty: i64,
    side: i8,           // 1=buy, -1=sell
    triggered: bool,
}
 
impl Algo for Sniper {
    fn on_book(&mut self, book: &L2Book, state: &AlgoState, _features: &OnlineFeatures, actions: &mut Actions) {
        if self.triggered { return; }
        if book.bid_ct == 0 || book.ask_ct == 0 { return; }
        
        let should_trigger = if self.side > 0 {
            // Buy when ask drops to target
            book.asks[0].px_1e9 <= self.target_price
        } else {
            // Sell when bid rises to target  
            book.bids[0].px_1e9 >= self.target_price
        };
        
        if should_trigger {
            self.next_id += 1;
            self.triggered = true;
            
            if self.side > 0 {
                // FOK: must fill entire quantity or reject
                actions.fok_buy(self.next_id, self.target_qty, self.target_price);
                log_warn!("SNIPER TRIGGERED: FOK BUY {} @ {}", 
                    self.target_qty, self.target_price);
            } else {
                actions.fok_sell(self.next_id, self.target_qty, self.target_price);
                log_warn!("SNIPER TRIGGERED: FOK SELL {} @ {}",
                    self.target_qty, self.target_price);
            }
        }
    }
 
    fn on_fill(&mut self, fill: &Fill, _state: &AlgoState) {
        log_warn!("SNIPER FILLED: {} @ {}", fill.qty_1e8, fill.px_1e9);
    }
 
    fn on_reject(&mut self, reject: &Reject) {
        log_error!("SNIPER REJECTED: {} - retrying next tick", reject.reason());
        self.triggered = false; // Allow retry
    }
    
    fn on_shutdown(&mut self, state: &AlgoState, actions: &mut Actions) {
        actions.cancel_all(state);
    }
}
 
export_algo!(Sniper {
    next_id: 0,
    target_price: 1_800_000_000_000, // $1.80
    target_qty: 1_000_000_000,       // 10 units
    side: 1,                          // Buying
    triggered: false,
});