ChebbiOS

Building a Rust Paper Trader

#rust#trading#async#project

I wanted to learn more about Rust and async programming, so I decided to build a text-based paper trading bot. The goal was simple: connect to Binance to get real price data but run only simulated trades, allowing me to test strategies without risking any actual money.

You can find the full source code on GitHub: ch09/rust_paper_trader.

The Goal

When learning a new language like Rust, it’s easy to get stuck in “tutorial hell.” I wanted a project that forced me to deal with:

  • Asynchronous I/O: Fetching data from an external API (Binance) in real-time.
  • State Management: Keeping track of a simulated wallet and open orders.
  • Error Handling: Making sure the bot doesn’t crash if the network hiccups.

System Architecture

The trading bot follows a classic Event-Driven Architecture. It doesn’t just run a script top-to-bottom; it spins up multiple asynchronous tasks that communicate via channels.

Rust Trading Bot Architecture

Core Components

  1. Market Data Stream (Ingestion): Using reqwest (or tungstenite for WebSockets), this task connects to the exchange and processes incoming payloads. It deserializes JSON into strictly typed Rust structs (MarketTick).

  2. Strategy Engine: This is the brain. It maintains a sliding window of historical prices. On every new tick, it updates the indicators (e.g., SMA-10 and SMA-50). If a “Golden Cross” (short-term crossing above long-term) is detected, it emits a Signal::Buy.

  3. Order Manager (Risk & Execution): This component manages the Virtual Wallet. It holds the state of USDT and BTC balances. When it receives a signal, it checks risk constraints (do I have enough balance? is the stop-loss hit?) before “executing” the simulated trade. All state changes are logged to stdout.

Why Rust & Tokio?

In Python, you might write this as a single while True loop. In Rust with Tokio, we can spawn these components as separate tasks. If the logging task hangs, the market data ingestion keeps running. If the API connection drops, we can restart just that task without losing the wallet state. This Actor-like model (communicating via mpsc channels) is incredibly robust.

Features

1. Real Data, Safe Simulation

The bot pulls live prices for Bitcoin (or any other pair) directly from Binance. However, all “trades” are purely internal. I implemented a fake wallet structure that tracks USDT and Crypto balances, simulating the mechanics of an exchange without the risk.

2. Simple Strategy

To keep things grounded, I implemented a classic Simple Moving Average (SMA) crossover strategy:

  • Buy Signal: When the short-term average price crosses above the long-term average.
  • Sell Signal: When the short-term average drops below the long-term average.

3. Safety Nets

Even in a simulation, risk management is key. I added automatic triggers:

  • Stop-Loss: Automatically sells if the price drops by 2% from the buy price.
  • Take-Profit: Automatically sells if the price rises by 5%.

Configuration

One of the cool things about Rust is how easy it can be to define strongly-typed configurations. I set up a config.rs to handle simulated parameters:

pub fn default() -> Self {
    Self {
        pair: "BTCUSDT".to_string(),
        interval: "1m".to_string(), 
        sma_short_period: 10,       
        sma_long_period: 50,       
        stop_loss_pct: 0.02,        
        initial_balance: 10000.0,   
    }
}

This makes it easy to tweak the strategy (e.g., changing the SMA periods) to see how it performs under different market conditions.

Conclusion

This project was a fantastic way to get hands-on with Rust’s ownership model and async capabilities. While this bot is just a simulation, the core logic—fetching data, analyzing signals, and managing state—is the foundation of any real algorithmic trading system.

Disclaimer: This is a learning project. Please don’t try to hook this up to a real wallet with real money without doing significant testing and security auditing!