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 --startThis 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:
| Example | Description |
|---|---|
| test-algo | Minimal callback + logging sanity check |
| mm-algo | Reference market-making strategy |
| speed-test-algo | Live round-trip and fill latency measurement |
| xrp-5usd-mm | Small-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,
});