trading-maid
English | 中文
trading-maid is a backtesting and live-trading framework for crypto futures, with a strong focus on behavior close to real exchanges. It includes key mechanics such as matching, slippage, leverage, margin, and liquidation for strategy validation, iteration, and live integration.
⚡ Keywords: high-fidelity matching / two-stage trigger orders / margin and liquidation mechanics / backtest visualization

Contents
- ✨ Core Capabilities
- 🧭 Trading Model and Constraints
- 🏗️ Architecture Overview
- 🚀 Quick Start
- 🧠 Context
- 📈 Indicators
- 🚦 Hook Intercept
- 🧪 A Complete Example
✨ Core Capabilities
- Backtesting environment close to live trading: simulates exchange matching logic to reduce backtest/live deviation.
- Live exchange abstraction: provides a unified exchange interface for smooth migration from backtest to live trading.
- Indicator and series tools: includes common technical indicators and time-series processing utilities.
- Backtest result visualization: render candlesticks, orders, and position history in a web page.
🧭 Trading Model and Constraints
🧾 Order Types
- Supported: trigger price + (limit | market)
- Not supported: OCO (take-profit/stop-loss combo order)
📦 Position Type
- Margin mode: isolated
- Position direction: one-way
- Margin asset type: single-currency margin
- Margin management: dynamically adjusts position margin
⚙️ Order Processing Logic
- Margin freeze: market orders freeze margin when filled; reduce-only orders do not freeze margin.
- Matching timing: an order is placed on the current k-line and matched on the next k-line. a trigger order will be matched immediately on the current k-line once.
- Fill rules: market orders fill at
Open; limit orders follow these rules:- Long
- limit >= market: fill at worst price
High - limit < market: fill at limit price
- limit >= market: fill at worst price
- Short
- limit <= market: fill at worst price
Low - limit > market: fill at limit price
- limit <= market: fill at worst price
- Long
- Priority: when both pending-order conditions and liquidation conditions are met, pending orders are executed first.
- Fees: market orders use
taker_fee; limit and liquidation orders usemaker_fee.
🏗️ Architecture Overview
For the full text diagram, see architecture.txt.
🚀 Quick Start
📥 Installation
Using cargo add
Or in Cargo.toml
[]
= "1.0.2"
🛠️ Usage
use *;
// Open a short position when a long upper shadow appears.
async
async
In this example, we set:
- minimum order size (min_size): 0.01
- minimum notional (min_notional): 0.0 = no restriction
- price tick size (tick_size): 0.1
- maker fee (maker_fee): 0.0002
- taker fee (taker_fee): 0.0005
- maintenance margin rate (maintenance): 0.004
- cash: 10000
- leverage: 10
- slippage: 0
The backtest runs on 1-minute data while the strategy runs on 1-hour k-lines. The engine calls the strategy at each 1-hour k-line close (the last minute of each hour), and every k-line observed by the strategy is 1-hour-level.
Although other levels can be used, you should always use 1-minute data as the backtest source to achieve high-precision results.
Use cargo run -r to run backtests faster.
🧠 Context
In Context, time, open, high, low, and close are of type &Series, which is essentially a wrapped slice.
You can use cx.close[0] for the current k-line close, cx.close[1] for the previous k-line close, and so on.
You can use cx.close[2..] to get a slice.
This type also overloads many operators, so you can omit index [0] in calculations, for example cx.close + 100.
You can destructure Context for easier OHLC usage.
async
📈 Indicators
Some commonly used indicators are built into indicator.
async
Calling ema directly may lead to slow backtests and incorrect calculations, because EMA depends on the previous EMA value. So it is recommended to use EMACache for fast and accurate calculation.
Use EMACache::with_ema to create an instance with an initial EMA value.
🚦 Hook Intercept
You can use hook to stop backtesting when a position is liquidated or an order is rejected (insufficient balance).
async
let mut engine = new;
engine.hook;
if let Err = engine.run.await
🧪 A Complete Example
use Arc;
use *;
// Stop backtesting when liquidation happens or an order is rejected (insufficient balance).
async
async