nanobook 0.9.0

Production-grade Rust execution infrastructure for automated trading: LOB engine, portfolio simulator, broker abstraction, risk engine
Documentation

nanobook

CI crates.io docs.rs License: MIT MIRI cargo-deny

Production-grade Rust execution infrastructure for automated trading. Zero-allocation hot paths. No panics on external input. MIRI-verified memory safety. Python computes the strategy. nanobook handles everything else.

Architecture

┌─────────────────────────────────────────────────┐
│        Your Python Strategy  (private)          │
│   Factors · Signals · Sizing · Scheduling       │
├─────────────────────────────────────────────────┤
│            nanobook  (Rust, open-source)         │
│  ┌──────────┬──────────┬──────────┬──────────┐  │
│  │  Broker  │   Risk   │Portfolio │   LOB    │  │
│  │   IBKR   │  Engine  │Simulator │  Engine  │  │
│  │  Binance │ PreTrade │ Backtest │ 8M ops/s │  │
│  └──────────┴──────────┴──────────┴──────────┘  │
│   Rebalancer CLI: weights → diff → execute      │
└─────────────────────────────────────────────────┘

Python computes what to trade — factor rankings, signals, target weights. nanobook executes how — order routing, risk checks, portfolio simulation, and a deterministic matching engine. Clean separation: strategy logic stays in Python, execution runs in Rust.

Workspace

Crate Description
nanobook LOB matching engine, portfolio simulator, backtest bridge
nanobook-broker Broker trait with IBKR and Binance adapters
nanobook-risk Pre-trade risk engine (position limits, leverage, short exposure)
nanobook-python PyO3 bindings for all layers
nanobook-rebalancer CLI: target weights → IBKR execution with audit trail

Install

Python:

pip install nanobook

Rust:

[dependencies]
nanobook = "0.9"

From source:

git clone https://github.com/ricardofrantz/nanobook
cd nanobook
cargo build --release
cargo test

# Python bindings
cd python && maturin develop --release

The Bridge: Python Strategy → Rust Execution

The canonical integration pattern — Python computes a weight schedule, Rust simulates the portfolio and returns metrics:

import nanobook

result = nanobook.py_backtest_weights(
    weight_schedule=[
        [("AAPL", 0.5), ("MSFT", 0.5)],
        [("AAPL", 0.3), ("NVDA", 0.7)],
    ],
    price_schedule=[
        [("AAPL", 185_00), ("MSFT", 370_00)],
        [("AAPL", 190_00), ("MSFT", 380_00), ("NVDA", 600_00)],
    ],
    initial_cash=1_000_000_00,  # $1M in cents
    cost_bps=15,                # 15 bps round-trip
    stop_cfg={"trailing_stop_pct": 0.05},
)

print(f"Sharpe: {result['metrics'].sharpe:.2f}")
print(f"Max DD: {result['metrics'].max_drawdown:.1%}")
print(result["holdings"][-1])    # per-period symbol weights
print(result["stop_events"])     # stop trigger metadata

Your optimizer produces weights. py_backtest_weights() handles rebalancing, cost modeling, position tracking, and return computation at compiled speed with the GIL released.

Clean aliases are also available for new callers: backtest_weights, capabilities, garch_forecast, optimize_*.

qtrade v0.4 Capability Gating

Use py_capabilities() and keep fallback logic in calc.bridge:

import nanobook

def has_nanobook() -> bool:
    try:
        import nanobook as _nb  # noqa: F401
        return True
    except Exception:
        return False

def nanobook_version() -> str | None:
    return getattr(nanobook, "__version__", None) if has_nanobook() else None

def has_nanobook_feature(name: str) -> bool:
    if not has_nanobook():
        return False

    caps = set(nanobook.py_capabilities()) if hasattr(nanobook, "py_capabilities") else set()
    if name in caps:
        return True

    # Symbol fallback for older builds
    symbol_map = {
        "backtest_stops": "py_backtest_weights",
        "garch_forecast": "py_garch_forecast",
        "optimize_min_variance": "py_optimize_min_variance",
        "optimize_max_sharpe": "py_optimize_max_sharpe",
        "optimize_risk_parity": "py_optimize_risk_parity",
        "optimize_cvar": "py_optimize_cvar",
        "optimize_cdar": "py_optimize_cdar",
        "backtest_holdings": "py_backtest_weights",
    }
    sym = symbol_map.get(name)
    return bool(sym and hasattr(nanobook, sym))

Layer Examples

LOB Engine (Rust)

use nanobook::{Exchange, Side, Price, TimeInForce};

let mut exchange = Exchange::new();
exchange.submit_limit(Side::Sell, Price(50_00), 100, TimeInForce::GTC);
let result = exchange.submit_limit(Side::Buy, Price(50_00), 100, TimeInForce::GTC);

assert_eq!(result.trades.len(), 1);
assert_eq!(result.trades[0].price, Price(50_00));

Portfolio + Metrics (Python)

portfolio = nanobook.Portfolio(1_000_000_00, nanobook.CostModel(commission_bps=5))
portfolio.rebalance_simple([("AAPL", 0.6)], [("AAPL", 150_00)])
portfolio.record_return([("AAPL", 155_00)])
metrics = portfolio.compute_metrics(252.0, 0.0)
print(f"Sharpe: {metrics.sharpe:.2f}")

Broker + Risk (Python)

# Pre-trade risk check
risk = nanobook.RiskEngine(max_position_pct=0.25, max_leverage=1.5)
checks = risk.check_order("AAPL", "buy", 100, 185_00,
                          equity_cents=1_000_000_00,
                          positions=[("AAPL", 200)])

# Execute through IBKR
broker = nanobook.IbkrBroker("127.0.0.1", 4002, client_id=1)
broker.connect()
oid = broker.submit_order("AAPL", "buy", 100, order_type="limit",
                          limit_price_cents=185_00)

Rebalancer CLI

# Build
cargo build -p nanobook-rebalancer --release

# Dry run — show plan without executing
rebalancer run target.json --dry-run

# Execute with confirmation prompt
rebalancer run target.json

# Compare actual vs target positions
rebalancer reconcile target.json

Performance

Single-threaded benchmarks (AMD Ryzen / Intel Core):

Operation Latency Throughput
Submit (no match) 120 ns 8.3M ops/sec
Submit (with match) ~200 ns 5M ops/sec
BBO query ~1 ns 1B ops/sec
Cancel (tombstone) 170 ns 5.9M ops/sec
L2 snapshot (10 levels) ~500 ns 2M ops/sec

Single-threaded throughput is roughly equivalent to Numba (both compile to LLVM IR). Where Rust wins: zero cold-start, true parallelism via Rayon with no GIL contention, and deterministic memory without GC pauses.

cargo bench

Feature Flags

Feature Default Description
event-log Yes Event recording for deterministic replay
serde No Serialize/deserialize all public types
persistence No File-based event sourcing (JSON Lines)
portfolio No Portfolio engine, position tracking, metrics, strategy trait
parallel No Rayon-based parallel parameter sweeps
itch No NASDAQ ITCH 5.0 binary protocol parser

Design Constraints

Engineering decisions that keep the system simple and fast:

  • Single-threaded — deterministic by design; same inputs always produce same outputs
  • In-process — no networking overhead; wrap externally if needed
  • No compliance layer — no self-trade prevention or circuit breakers (out of scope)
  • No complex order types — no iceberg or pegged orders

Documentation

  • DOC.md — Developer reference: full API, types, patterns, advanced usage
  • docs.rs — Rust API docs

License

MIT