Skip to main content

Overview

NanoARB’s trait-based architecture allows you to implement custom strategies, order books, risk managers, and other trading components. All traits are designed to be thread-safe (Send + Sync) for concurrent execution.

Strategy

Implement custom trading strategies.

Definition

pub trait Strategy: Send + Sync {
    fn name(&self) -> &str;
    fn on_market_data(&mut self, book: &dyn OrderBook) -> Vec<Order>;
    fn on_fill(&mut self, fill: &Fill);
    fn on_order_ack(&mut self, order_id: OrderId);
    fn on_order_reject(&mut self, order_id: OrderId, reason: &str);
    fn on_order_cancel(&mut self, order_id: OrderId);
    fn position(&self) -> i64;
    fn pnl(&self) -> f64;
    fn is_ready(&self) -> bool;
    fn reset(&mut self);
}

Required Methods

name
fn(&self) -> &str
Strategy name for logging and metrics
fn name(&self) -> &str {
    "MyStrategy"
}
on_market_data
fn(&mut self, book: &dyn OrderBook) -> Vec<Order>
Called on each market data update. Return orders to submit.
fn on_market_data(&mut self, book: &dyn OrderBook) -> Vec<Order> {
    let mut orders = Vec::new();
    
    if let Some((bid_price, bid_qty)) = book.best_bid() {
        if let Some((ask_price, ask_qty)) = book.best_ask() {
            // Implement your strategy logic here
            if should_trade(bid_price, ask_price) {
                let order = Order::new_limit(
                    self.next_order_id(),
                    self.instrument_id,
                    Side::Buy,
                    bid_price,
                    Quantity::new(10),
                    TimeInForce::GTC,
                );
                orders.push(order);
            }
        }
    }
    
    orders
}
on_fill
fn(&mut self, fill: &Fill)
Called when an order is filled
fn on_fill(&mut self, fill: &Fill) {
    self.position += fill.signed_quantity();
    self.realized_pnl -= fill.fee;
}
on_order_ack
fn(&mut self, order_id: OrderId)
Called when an order is acknowledged by the exchange
fn on_order_ack(&mut self, order_id: OrderId) {
    self.pending_orders.remove(&order_id);
    self.active_orders.insert(order_id);
}
on_order_reject
fn(&mut self, order_id: OrderId, reason: &str)
Called when an order is rejected
fn on_order_reject(&mut self, order_id: OrderId, reason: &str) {
    eprintln!("Order {} rejected: {}", order_id, reason);
    self.pending_orders.remove(&order_id);
}
on_order_cancel
fn(&mut self, order_id: OrderId)
Called when an order is cancelled
fn on_order_cancel(&mut self, order_id: OrderId) {
    self.active_orders.remove(&order_id);
}
position
fn(&self) -> i64
Get current position (positive = long, negative = short)
fn position(&self) -> i64 {
    self.position
}
pnl
fn(&self) -> f64
Get current profit and loss
fn pnl(&self) -> f64 {
    self.realized_pnl + self.unrealized_pnl
}
is_ready
fn(&self) -> bool
Check if strategy is ready to trade
fn is_ready(&self) -> bool {
    self.initialized && self.risk_checks_passed()
}
reset
fn(&mut self)
Reset strategy state
fn reset(&mut self) {
    self.position = 0;
    self.realized_pnl = 0.0;
    self.active_orders.clear();
}

When to Implement

Implement the Strategy trait to:
  • Create custom algorithmic trading strategies
  • Implement market making logic
  • Build arbitrage strategies
  • Develop ML-based trading systems

OrderBook

Access market depth and order book state.

Definition

pub trait OrderBook: Send + Sync {
    fn best_bid(&self) -> Option<(Price, Quantity)>;
    fn best_ask(&self) -> Option<(Price, Quantity)>;
    fn mid_price(&self) -> Option<Price>;
    fn spread(&self) -> Option<Price>;
    fn quote(&self) -> Option<Quote>;
    fn bid_at_level(&self, level: usize) -> Option<(Price, Quantity)>;
    fn ask_at_level(&self, level: usize) -> Option<(Price, Quantity)>;
    fn bid_depth(&self, levels: usize) -> Quantity;
    fn ask_depth(&self, levels: usize) -> Quantity;
    fn timestamp(&self) -> Timestamp;
}

Required Methods

best_bid
fn(&self) -> Option<(Price, Quantity)>
Get the best bid price and quantity
fn best_bid(&self) -> Option<(Price, Quantity)> {
    self.bids.first().map(|level| (level.price, level.quantity))
}
best_ask
fn(&self) -> Option<(Price, Quantity)>
Get the best ask price and quantity
fn best_ask(&self) -> Option<(Price, Quantity)> {
    self.asks.first().map(|level| (level.price, level.quantity))
}
mid_price
fn(&self) -> Option<Price>
Get the mid price (average of best bid and ask)
fn mid_price(&self) -> Option<Price> {
    match (self.best_bid(), self.best_ask()) {
        (Some((bid, _)), Some((ask, _))) => {
            Some(Price::from_raw((bid.raw() + ask.raw()) / 2))
        }
        _ => None,
    }
}
spread
fn(&self) -> Option<Price>
Get the spread in ticks
fn spread(&self) -> Option<Price> {
    match (self.best_bid(), self.best_ask()) {
        (Some((bid, _)), Some((ask, _))) => {
            Some(Price::from_raw(ask.raw() - bid.raw()))
        }
        _ => None,
    }
}
quote
fn(&self) -> Option<Quote>
Get the current quote (BBO)
bid_at_level
fn(&self, level: usize) -> Option<(Price, Quantity)>
Get price at a specific bid level (0 = best)
fn bid_at_level(&self, level: usize) -> Option<(Price, Quantity)> {
    self.bids.get(level).map(|l| (l.price, l.quantity))
}
ask_at_level
fn(&self, level: usize) -> Option<(Price, Quantity)>
Get ask at a specific level (0 = best)
bid_depth
fn(&self, levels: usize) -> Quantity
Get total quantity at bid levels up to depth
fn bid_depth(&self, levels: usize) -> Quantity {
    self.bids.iter()
        .take(levels)
        .fold(Quantity::ZERO, |acc, level| acc + level.quantity)
}
ask_depth
fn(&self, levels: usize) -> Quantity
Get total quantity at ask levels up to depth
timestamp
fn(&self) -> Timestamp
Get the current timestamp

When to Implement

Implement the OrderBook trait to:
  • Create custom order book implementations
  • Build order book aggregators across exchanges
  • Implement synthetic order books for testing
  • Create order book replay systems from historical data

FillModel

Simulate order fills for backtesting.

Definition

pub trait FillModel: Send + Sync {
    fn try_fill(
        &self,
        order: &Order,
        book: &dyn OrderBook,
        current_time: Timestamp,
    ) -> Option<(Price, Quantity)>;
    
    fn fill_probability(
        &self,
        queue_position: usize,
        level_quantity: Quantity,
    ) -> f64;
}

Required Methods

try_fill
fn(&self, order: &Order, book: &dyn OrderBook, current_time: Timestamp) -> Option<(Price, Quantity)>
Simulate a fill attempt for an order. Returns fill price and quantity if filled.
fn try_fill(
    &self,
    order: &Order,
    book: &dyn OrderBook,
    current_time: Timestamp,
) -> Option<(Price, Quantity)> {
    match order.order_type {
        OrderType::Market => {
            // Market orders fill immediately at best price
            match order.side {
                Side::Buy => book.best_ask()
                    .map(|(price, qty)| (price, qty.min(order.quantity))),
                Side::Sell => book.best_bid()
                    .map(|(price, qty)| (price, qty.min(order.quantity))),
            }
        }
        OrderType::Limit => {
            // Limit orders fill when price crosses
            self.try_fill_limit(order, book)
        }
        _ => None,
    }
}
fill_probability
fn(&self, queue_position: usize, level_quantity: Quantity) -> f64
Get probability of being filled at a given queue position
fn fill_probability(
    &self,
    queue_position: usize,
    level_quantity: Quantity,
) -> f64 {
    if queue_position == 0 {
        return 1.0;
    }
    
    let queue_ratio = queue_position as f64 / level_quantity.as_f64();
    (1.0 - queue_ratio).max(0.0)
}

When to Implement

Implement the FillModel trait to:
  • Create realistic fill simulations for backtesting
  • Model queue position and adverse selection
  • Simulate different market conditions
  • Test strategies under various fill assumptions

RiskManager

Manage position and risk limits.

Definition

pub trait RiskManager: Send + Sync {
    fn check_order(&self, order: &Order, current_position: i64) -> Result<(), Error>;
    fn check_position(&self, position: i64) -> Result<(), Error>;
    fn check_drawdown(&self, pnl: f64, peak_pnl: f64) -> Result<(), Error>;
    fn should_kill_switch(&self, pnl: f64, position: i64) -> bool;
    fn max_position(&self) -> i64;
    fn max_order_size(&self) -> u32;
}

Required Methods

check_order
fn(&self, order: &Order, current_position: i64) -> Result<(), Error>
Check if an order passes risk checks
fn check_order(&self, order: &Order, current_position: i64) -> Result<(), Error> {
    // Check order size
    if order.quantity.value() > self.max_order_size() {
        return Err(Error::RiskLimitExceeded(
            format!("Order size {} exceeds max {}", 
                    order.quantity.value(), self.max_order_size())
        ));
    }
    
    // Check resulting position
    let new_position = current_position + 
        (order.quantity.as_i64() * order.side.sign());
    self.check_position(new_position)?;
    
    Ok(())
}
check_position
fn(&self, position: i64) -> Result<(), Error>
Check if position limits are breached
fn check_position(&self, position: i64) -> Result<(), Error> {
    let abs_position = position.abs();
    if abs_position > self.max_position() {
        return Err(Error::RiskLimitExceeded(
            format!("Position {} exceeds max {}", 
                    abs_position, self.max_position())
        ));
    }
    Ok(())
}
check_drawdown
fn(&self, pnl: f64, peak_pnl: f64) -> Result<(), Error>
Check if drawdown limits are breached
fn check_drawdown(&self, pnl: f64, peak_pnl: f64) -> Result<(), Error> {
    let drawdown = peak_pnl - pnl;
    let drawdown_pct = if peak_pnl > 0.0 {
        drawdown / peak_pnl
    } else {
        0.0
    };
    
    if drawdown_pct > self.max_drawdown_pct {
        return Err(Error::RiskLimitExceeded(
            format!("Drawdown {:.2}% exceeds max {:.2}%",
                    drawdown_pct * 100.0, self.max_drawdown_pct * 100.0)
        ));
    }
    Ok(())
}
should_kill_switch
fn(&self, pnl: f64, position: i64) -> bool
Check if we should kill all positions (emergency stop)
fn should_kill_switch(&self, pnl: f64, position: i64) -> bool {
    pnl < self.kill_switch_loss || position.abs() > self.kill_switch_position
}
max_position
fn(&self) -> i64
Get maximum allowed position
max_order_size
fn(&self) -> u32
Get maximum allowed order size

When to Implement

Implement the RiskManager trait to:
  • Enforce position and order size limits
  • Implement custom risk rules
  • Create dynamic risk management based on market conditions
  • Add compliance checks

ExecutionHandler

Handle order submission and management.

Definition

pub trait ExecutionHandler: Send + Sync {
    fn submit_order(&mut self, order: Order) -> Result<OrderId, Error>;
    fn cancel_order(&mut self, order_id: OrderId) -> Result<(), Error>;
    fn modify_order(
        &mut self,
        order_id: OrderId,
        new_price: Option<Price>,
        new_quantity: Option<Quantity>,
    ) -> Result<(), Error>;
    fn get_order(&self, order_id: OrderId) -> Option<&Order>;
    fn active_orders(&self) -> Vec<&Order>;
}

When to Implement

Implement the ExecutionHandler trait to:
  • Connect to exchange APIs
  • Create paper trading simulators
  • Build order routing systems
  • Implement order management systems (OMS)

LatencyModel

Model network latency for backtesting.

Definition

pub trait LatencyModel: Send + Sync {
    fn order_latency(&self) -> i64;
    fn market_data_latency(&self) -> i64;
    fn ack_latency(&self) -> i64;
    fn reset(&mut self);
}

When to Implement

Implement the LatencyModel trait to:
  • Simulate network latency in backtests
  • Test strategy sensitivity to latency
  • Model different network conditions
  • Create realistic simulation environments

FeeModel

Calculate trading fees.

Definition

pub trait FeeModel: Send + Sync {
    fn calculate_fee(
        &self,
        price: Price,
        quantity: Quantity,
        is_maker: bool,
        side: Side,
    ) -> f64;
}

When to Implement

Implement the FeeModel trait to:
  • Model exchange-specific fee structures
  • Account for maker/taker fees
  • Implement tiered fee schedules
  • Calculate fee rebates

ModelInference

Run machine learning model inference.

Definition

pub trait ModelInference: Send + Sync {
    type Input;
    type Output;
    
    fn predict(&self, input: &Self::Input) -> Result<Self::Output, Error>;
    fn name(&self) -> &str;
    fn expected_latency_ns(&self) -> u64;
}

When to Implement

Implement the ModelInference trait to:
  • Integrate ML models into strategies
  • Run real-time predictions
  • Benchmark model latency
  • A/B test different models

MetricsCollector

Collect performance and operational metrics.

Definition

pub trait MetricsCollector: Send + Sync {
    fn record_latency(&self, name: &str, latency_ns: u64);
    fn increment_counter(&self, name: &str, value: u64);
    fn set_gauge(&self, name: &str, value: f64);
    fn record_pnl(&self, pnl: f64);
    fn record_fill(&self, side: Side, quantity: Quantity, is_maker: bool);
}

When to Implement

Implement the MetricsCollector trait to:
  • Export metrics to monitoring systems (Prometheus, DataDog)
  • Log performance data
  • Track strategy metrics
  • Monitor system health